Java 학습 노트 45(다중 스레드 2: 보안 문제 및 해결 원리)

11336 단어
스레드 보안 문제 및 해결 방법:
여러 개의 스레드가 한 개의 공유 데이터를 사용할 때 안전 문제가 발생한다
 
대표적인 사례:
영화관 매표소 는 모두 100석, 최대 100장, 매표 방식 이 다양하다
세 가지 방식으로 같은 공유 데이터를 조작하면 안전 문제가 발생할 수 있다.
예:
package demo1;

public class Tickets implements Runnable {
    private int ticket = 100;
    
    public void run(){
        while(true){
            if (ticket>0) {
                System.out.println(Thread.currentThread().getName()+"   "+ticket--+"  ");
            }
        }
    }
}
package demo1;

public class ThreadDemo {
    public static void main(String[] args) {
        Tickets t = new Tickets();
        Thread t0 = new Thread(t);
        Thread t1 = new Thread(t);
        Thread t2 = new Thread(t);
        t0.start();
        t1.start();
        t2.start();
    }
}

일반적으로 문제가 생기지 않지만, 이런 문제를 생각해야 한다.
그러나 마지막 표만 남았다고 가정하면 한 라인이 CPU 자원 집행을 뺏는다. 판단이 끝날 때 CPU 자원은 다른 라인에 뺏기고 다른 라인이 판단하고 집행한다.
이때 시작할 때의 노선이 이미 판단이 끝났기 때문에 계속 집행하면 표 수가 마이너스로 변하기 때문에 여기에 문제가 생겼다
 
해결 방법:
동기화 코드 블록
원리: 한 라인이 데이터 조작에 들어갈 때 다른 라인의 집행을 막는다
package demo1;

public class Tickets implements Runnable {
    private int ticket = 100;
    private Object obj1 = new Object();
    public void run() {
        while (true) {
            synchronized (obj1) {
                if (ticket > 0) {
                    System.out.println(Thread.currentThread().getName() + "   " + ticket-- + "  ");
                }
            }
        }
    }
}

하지만 안전했지만 운행 속도가 떨어졌다
그러나 우리는 안전성을 위해 속도를 고려하지 않고 어쨌든 안전성을 확보해야 한다
 
여기에 전송된 대상 매개 변수는 약칭: 동기화 자물쇠, 전문 이름: 대상 모니터
원리:
자물쇠가 없는 라인은 실행할 수 없고 기다릴 수 있습니다
루틴이 동기화 코드 블록을 만났을 때 동기화 자물쇠가 있는지 판단합니다. 만약 있다면 자물쇠를 가져가서 동기화에 들어가서 실행하고 실행이 끝난 후에 자물쇠 대상을 돌려보냅니다.
다른 스레드가 코드 블록을 만났을 때 자물쇠가 없어서 들어갈 수 없습니다. 원래의 스레드가 자물쇠를 돌려준 후에 새로운 스레드가 자물쇠를 가져와 순환합니다
 
여기서 분명히 알 수 있듯이, 이렇게 많은 과정 에서 속도 는 자연히 느려졌다
 
동기화 방식으로 문제 해결:
장점: 낮은 코드 수
package demo1;

public class Tickets implements Runnable {
    private int ticket = 100;
    
    public void run() {
        while (true) {
            payTicket();
        }
    }

    public synchronized void payTicket() {
        //               :  this
        //           .class
        if (ticket > 0) {
            System.out.println(Thread.currentThread().getName() + "   " + ticket-- + "  ");
        }
    }
}

단점: 이상이 발생하면 방법의 자물쇠 대상이 방출되지 않고 동기화되지 않으며 자물쇠가 방출되지 않습니다
 
여기에는 Lock 인터페이스가 필요합니다.
더욱 광범위한 잠금 작업 제공
 
이전 티켓 판매 사례 개선:
package demo1;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Tickets implements Runnable {
    private int ticket = 100;
    private Lock lock = new ReentrantLock();

    public void run() {
        while (true) {
            lock.lock();
            if (ticket > 0) {
                System.out.println(Thread.currentThread().getName() + "   " + ticket-- + "  ");
            }
            lock.unlock();
        }
    }
}

 
고정 자물쇠:
동기식 잠금으로 인한 폐단:
스레드 작업에 여러 개의 동기화가 생겼을 때 동기화에 다른 동기화가 끼워져 있으면 이때 일종의 현상을 일으키고 프로그램에 무한한 기다림이 나타난다. 이런 현상을 사쇄라고 부른다.
두 사람이 국수 한 그릇을 먹는데 젓가락이 한 켤레밖에 없다. 두 사람이 한 젓가락을 뺏으면 손으로 잡지 못하도록 규정되어 있다. 이때 국수를 먹을 수 없다.
코드 구현:
package demo1;

public class LockA {
    private LockA(){}
    
    public final static LockA locka =new LockA();
}
package demo1;

public class LockB {
    private LockB(){}
    
    public final static LockB lockb =new LockB();
}
package demo1;

public class DeadLock implements Runnable {
    private int i = 0;

    public void run() {
        while (true) {
            if (i % 2 == 0) {
                synchronized (LockA.locka) {
                    System.out.println("if...locka");
                    synchronized (LockB.lockb) {
                        System.out.println("if...lockb");
                    }
                }
            } else {
                synchronized (LockB.lockb) {
                    System.out.println("else...lockb");
                    synchronized (LockA.locka) {
                        System.out.println("else...locka");
                    }
                }
            }
            i++;
        }
    }
}
package demo1;

public class DeadLockDemo {
    public static void main(String[] args) {
        DeadLock dead = new DeadLock();
        Thread t0 = new Thread(dead);
        Thread t1 = new Thread(dead);
        t0.start();
        t1.start();
    }
}

운행 후 어느 곳에 끼어 움직이지 않지만 멈추지 않는 것을 발견하였다
 
전재 대상:https://www.cnblogs.com/xuyiqing/p/8320162.html

좋은 웹페이지 즐겨찾기