자바 멀티 스레딩 - 동기화 된 변수와 동시 액세스

비 스레드 안전도, 같은 개체에 여러 스레드에 의한 동시 액세스의 경우 결과를 결과하는 것은 읽기에는 너무 더럽, 인스턴스 변수했다, 즉, 데이터가 실제로 좋은 변화 촬영하는 것입니다.

변수가 내부 방법은 개인이었다 경우 비 스레드 안전 문제의 "인스턴스 변수"존재 "- 스레드 안전 비,"문제가 존재하지 않습니다.

1 동기화

1.1 동기화 方法

public class ThreadTest {
    public static void main(String[] args) {
        Add add = new Add();
        Add add1 = new Add();
        ThreadAA threadAA = new ThreadAA(add);
        threadAA.start();
        ThreadBB threadBB = new ThreadBB(add1);
        threadBB.start();
    }
}

class ThreadAA extends Thread{
    private Add a;
    public ThreadAA(Add add){
        this.a = add;
    }
    @Override
    public void run(){
        a.add("a");
    }
}

class ThreadBB extends Thread{
    private Add b;
    public ThreadBB(Add add){
        this.b = add;
    }
    @Override
    public void run(){
        b.add("b");
    }
}

class Add{
    private int num = 0;
    //同步方法
    synchronized public void add(String username){
        try{
            if (username.equals("a")){
                num = 100;
                System.out.println("add a end");
                Thread.sleep(2000);
            }else {
                num = 200;
                System.out.println("add b end");
            }
            System.out.println(username + " name " + num);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
复制代码

인쇄 결과

add a end
add b end
b name 200
a name 100
复制代码

인쇄 명령의 결과에서 있기 때문에, 동기화, 그러나 십자가되지 잠금 달성 동기화 객체 잠금 키워드입니다 . 다수의 스레드가 동일한 개체에 액세스하는 경우에있어서 상기 예 그래서 동기화 키워드 제 스레드 실행에있어서의 로크 개체에 속하는 유지 실 후 다른 스레드 만 한 상태에서 대기 할 수있다.

객체 잠금 잠금을 보유의 동기화 방법을 확인

//将上面的ThreadTest类中的main方法进行修改
public class ThreadTest {
    public static void main(String[] args) {
        Add add = new Add();
//        Add add1 = new Add();
        ThreadAA threadAA = new ThreadAA(add);
        threadAA.start();
        ThreadBB threadBB = new ThreadBB(add);
        threadBB.start();
    }
}
复制代码

영업 실적

add a end
a name 100
add b end
b name 200
复制代码

작업의 더 많은 결과를 볼 수있는이 시간은 인쇄의 순서입니다.

1.2 동기 동기 블록

단점은 스레드가 작업을 수행하는 데 오랜 시간 동기화 메소드를 호출 등이 있습니다 동기화 방법 위에서 언급하지만, 일부의 경우에 선언 동기화 방법을 사용하여, 다른 스레드는 오랫동안 기다려야합니다. 이러한 경우, 우리는 코드 섹션은 동 기적으로 수행해야 포장 동기화 동기화 블록을 이용하여 해결하는 동기 블록의 동기 부호를 사용할 수있다.

public class ThreadFunction {
    public static void main(String[] args) {
        ObjFunction objFunction = new ObjFunction();
        FunA funA = new FunA(objFunction);
        funA.setName("a");
        funA.start();
        FunB funB = new FunB(objFunction);
        funB.setName("b");
        funB.start();

    }
}

class FunB extends Thread{
    private ObjFunction objFunction;
    public FunB(ObjFunction objFunction){
        this.objFunction = objFunction;
    }
    @Override
    public void run(){
        objFunction.objMethod();
    }
}

class FunA extends Thread{
    private ObjFunction objFunction;
    public FunA(ObjFunction objFunction){
        this.objFunction = objFunction;
    }
    @Override
    public void run(){
        objFunction.objMethod();
    }
}

class ObjFunction{
    public void objMethod(){
        try{
            System.out.println(Thread.currentThread().getName() + " start");
            synchronized (this) {
                System.out.println("start time = " + System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("end time = "+ System.currentTimeMillis());
            }
            System.out.println(Thread.currentThread().getName() + " end");
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
复制代码

영업 실적

a start
b start
start time = 1559033466082
end time = 1559033468083
a end
start time = 1559033468083
end time = 1559033470084
b end
复制代码

알 수있는 바와 같이, 추가 코드 동기 블록은 동기 부호 블록은 동기 행하고, 비동기식이다. 그리고 동기 (이) 잠겨 객체는 현재 개체입니다.

뿐만 아니라 this잠금 개체로, 자바는 또한 잠금 기능을 동기화 할 수 있지만, 동기화 모니터와 같은 객체, 또는 다른 결과가 비동기 호출입니다 실행해야합니다주의하는 어떠한 객체를 지원합니다.

1.3 동기화 정적 동기화 방법

synchronized 키워드 정적은 정적 방법에 적용 할 수 있도록 현재 해당 클래스 * 로크 대상으로 된 .java 클래스 파일.

로크 대상 지주 스태틱 동기 = 동기화 방법 (클래스)

public class ThreadTest {
    public static void main(String[] args) {
        ThreadAA threadAA = new ThreadAA();
        threadAA.start();
        ThreadBB threadBB = new ThreadBB();
        threadBB.start();
    }
}

class ThreadAA extends Thread{
    @Override
    public void run(){
        Add.add("a");
    }
}

class ThreadBB extends Thread{
    @Override
    public void run(){
        Add.add("b");
    }
}

class Add{
    private static int num = 0;
    //同步方法
    synchronized static public void add(String username){
        try{
            if (username.equals("a")){
                num = 100;
                System.out.println("add a end");
                Thread.sleep(2000);
            }else {
                num = 200;
                System.out.println("add b end");
            }
            System.out.println(username + " name " + num);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
复制代码

영업 실적

add a end
a name 100
add b end
b name 200
复制代码

1.4 동기 类

클래스의 키워드 동기 수정을 사용하여, 클래스 메소드는 코드가 표시되지 않는 모든 동기 방법이다.

1.5 동기화 잠금 재진입

쓰레드 로크 대상이 개체가 다시 잠글 수있다 다시 대물 로크 요청을 제공 할 때 사용 동기화 재진입 잠금 기능, 즉 동기화있다. 내부 동기화 방법 / 코드 블록이 다른 동기화 / 블록의 본의 메소드를 호출 할 때 즉, 당신은 항상 얻을 수 있습니다.

public class ThreadAgain {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                new Service().service1();
            }
        }).start();
    }
}

class Service{
    synchronized public void service1(){
        System.out.println("service1");
        service2();
    }

    synchronized private void service2() {
        System.out.println("service2");
        service3();
    }

    synchronized private void service3() {
        System.out.println("service3");
    }

}
复制代码

영업 실적

service1
service2
service3
复制代码

휘발성이

휘발성 키워드의 주요 역할은 여러 스레드 사이의 변수에 표시됩니다.

오히려 전용 데이터 스택 스레드로부터 취득한 변수의 값보다 일반적인 스택에서 변수의 값을 얻기 위해 필수적이다. 여러 스레드에서, 스택 및 프로그램 카운터는 개인이며, 스택 및 전역 변수가 노력하고 있습니다.

코드를 봐

public class MyVolatile {
    public static void main(String[] args) {
        try {
            RunThread runThread = new RunThread();
            runThread.start();
            Thread.sleep(2000);
            runThread.setRun(false);
            System.out.println("为runThread复制false");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

}

class RunThread extends Thread{
    private boolean isRun = true;

    public boolean isRun() {
        return isRun;
    }

    public void setRun(boolean run) {
        isRun = run;
    }

    @Override
    public void run(){
        System.out.println("进入了run方法");
        while (isRun == true){
        }
        System.out.println("退出run方法,线程停止");
    }
}
复制代码

당신은 콘솔에서 볼 수있는 스레드는 끝나지 않습니다. 문제는 개인 스택과 작품의 스택의 값을 사용하면 휘발성 키워드를 사용하여이 문제를 해결하기 위해 노력에 의한 동기의 값을 가지고 있다는 것입니다.

클래스 코드를 수정 RunThread

class RunThread extends Thread{
   volatile private boolean isRun = true;

    public boolean isRun() {
        return isRun;
    }

    public void setRun(boolean run) {
        isRun = run;
    }

    @Override
    public void run(){
        System.out.println("进入了run方法");
        while (isRun == true){
        }
        System.out.println("退出run方法,线程停止");
    }
}
复制代码

다시 실행, 정상의 끝을 스레드.

휘발성 키워드는 여러 스레드 사이의 인스턴스 변수에서 볼 수 있지만, 원자 지원하지 않는 휘발성 치명적인 결함이 있지만.

인증 휘발성 자성을 지원하지 않습니다

public class IsAtomic {
    public static void main(String[] args) {
        MyAtomicRun[] myAtomicRuns = new MyAtomicRun[100];
        for (int i = 0;i<100;i++){
            myAtomicRuns[i] = new MyAtomicRun();
        }
        for (int i = 0;i<100;i++){
            myAtomicRuns[i].start();
        }
    }
}

class MyAtomicRun extends Thread{
    volatile public static int count;
    private static void count(){
        for (int i = 0;i<100;i++){
            count++;
        }
        System.out.println("count: " + count);
    }
    @Override
    public void run(){
        count();
    }
}
复制代码

인쇄 출력

//篇幅较长,没有全部粘贴
count: 5000
count: 4900
count: 4800
count: 4700
count: 4600
count: 4500
count: 4400
count: 4400
复制代码

뷰의 출력에서의 결과는 어떠한 출력 만하고 이상적인 상태가 아니다.

코드 개선

class MyAtomicRun extends Thread{
    volatile public static int count;
    //需要使用同步静态方法,这样是以class为锁,才能达到同步效果
    synchronized private static void count(){
        for (int i = 0;i<100;i++){
            count++;
        }
        System.out.println("count: " + count);
    }
    @Override
    public void run(){
        count();
    }
}
复制代码

인쇄 출력

count: 9300
count: 9400
count: 9500
count: 9600
count: 9700
count: 9800
count: 9900
count: 10000
复制代码

이 시간은, 출력은 정확한 결과이다.

멀티 스레딩 공유 변수를 읽을 때 인스턴스 변수를 인식 할 수있는 여러 스레드에서 휘발성 키워드의 주요 사용이 변경되는 경우, 당신은 즉, 최신 값이 사용됩니다 얻을 수 있습니다, 당신은 최신 값을 얻을 수 있습니다.

휘발성 및 동기의 2.1 비교

  1. 경량 구현 휘발성 스레드 동기화 때문에 성능이 확실히 동기 휘발성 변수 만 수정 및 동기화 방법, 및 블록 코드가 수정 될 수 될 수있는 것보다 더 낫다.
  2. 휘발성 차단 액세스를 멀티 스레드 때 발생하고,이 차단됩니다 동기화되지 않습니다.
  3. 휘발성 데이터의 가시성을 보장하지만, 자성을 보장하지 않으려면, 모두 동기화 원자 보장, 또는 데이터가 개인 메모리와 일반 메모리의 동기화를 동기화하기 때문에 간접적으로 가시성을 보장합니다.
  4. 용액 변수 다중 스레드 공개 중 휘발성이고 동기화 솔루션은 여러 스레드간에 동기화 리소스에 액세스 할 수있다.

2.2 변수 작업 메모리

상기 휘발성 키워드 변수 수정 이러한 동작 ++ 오퍼레이터가 실제로 원자 조작 아니다 마찬가지로 스레드 안전하지 않는다.

나는 단계를 ++ :

  1. 나는 메모리에서 값을 취
  2. 나는 계산
  3. 나는 메모리에 기록됩니다

다른 스레드의 연산의 두번째 단계의 시간은 또한 I의 값을 변경하는 경우, 시간은 오래된 데이터가 될 것이다.

17138799-194122859a667229.png
의 image.png

<figcaption의> </ figcaption의>

  • 판독 단계를로드 현재 스레드 메인 메모리 복사 변수에서 작업 메모리;
  • 공유 변수의 값을 변경, 실행 코드; 사용 단계를 할당
  • 저장 및 기록 단계 : 작업 메모리와 메인 데이터의 변수 값에 대응하는 리프레시하는 메모리.

멀티 스레드 환경에서 사용 할당 여러 번 발생하지만, 주기억 변수의 값이 변경되는 경우에 그 위에 로딩 된 바와 같이,이 동작은, 스테이지를 읽은 후, 즉, 메모리의 작업 스레드 원자 아니다 그래서 동일한 비 스레드 안전성에 문제가없는 전용 메모리 및 메모리 공용 변수 값은 동기 계산 결과를 예상하지 않는 원인이 상응하는 변화를 생성하지 않을 것이다.

휘발성 키워드 수정 변수의 경우, JVM에만 보장 값은 최신 상태로 작업 메모리를 메인 메모리에서로드됩니다.

HTTPS : //www.jianshu.com/p/bd8877ad99d8 재현

추천

출처blog.csdn.net/weixin_33813128/article/details/91303300