1. 程式人生 > >Java多執行緒學習筆記(一) synchronized同步方法

Java多執行緒學習筆記(一) synchronized同步方法

synchronized同步方法

1.提出問題-例項變數非執行緒安全

如果多個執行緒同時訪問1個物件的例項變數,則可能出現"非執行緒安全"問題。

1.1 何為非執行緒安全?

我的理解是多個執行緒對一個例項變數操作會出現值被更改,不同步的情況。

1.2 舉例

1.2.1 有私有變數的類HasPrivateNum (供多執行緒們去呼叫)

public class HasPrivateNum {
    private int num = 0;
    public void addNum(String userName){
        try{
            if (userName.equalsIgnoreCase("wang.dong")){
                num = 1;
                System.out.println("wang dong set over");
                Thread.
sleep(2000); }else { num = 2; System.out.println("other man set over"); } System.out.println(userName + " num = " + num); }catch (InterruptedException e){ e.printStackTrace(); } } }

這個類裡面有一個私有變數 num,在addNum()函式里根據傳的userName不同而賦值.

1.2.2 執行緒A

public class ThreadA  extends Thread{
    
    private HasPrivateNum num;

    public ThreadA(HasPrivateNum num){
        super();
        this.num = num;
    }
    
    @Override
    public void run(){
        super.run();
        num.addNum("wang.dong");
    }
}

1.2.3 執行緒B

public class ThreadB extends Thread{
    private HasPrivateNum num;

    public ThreadB(HasPrivateNum num){
        super();
        this.num = num;
    }

    @Override
    public void run(){
        super.run();
        num.addNum("other man");
    }
}

1.2.4 開啟AB執行緒共同訪問1個物件

public class Run {
    public static void main(String[] args) {
        HasPrivateNum numRef = new HasPrivateNum();

        //A
        ThreadA aThread = new ThreadA(numRef);
        aThread.start();

        //B
        ThreadB bThread = new ThreadB(numRef);
        bThread.start();
    }
}

1.2.5 執行結果(多次執行有機率出現如下結果)

在這裡插入圖片描述
A和B這2個執行緒同時訪問一個沒有同步的方法,並給裡面變數賦值,可能出現"非執行緒安全"問題

2. 解決問題-給方法上加synchronized

2.1 synchronized修飾範圍

修飾物件 作用範圍 作用物件
程式碼塊 synchronized(this){}內 呼叫這個程式碼塊的物件
方法(介面方法/構造方法不行) 整個方法 呼叫這個方法的例項物件
靜態方法 整個方法 這個類的所有類物件(共用一把鎖)
synchronized後面{} 這個類的所有物件(共用一把鎖)

2.2 修改後的HasPrivateNum類

public class HasPrivateNum {
    private int num = 0;
    
    // add synchroized
    public synchronized void addNum(String userName){
        try{
            if (userName.equalsIgnoreCase("wang.dong")){
                num = 1;
                System.out.println("wang dong set over");
                Thread.sleep(2000);
            }else {
                num = 2;
                System.out.println("other man set over");
            }
            System.out.println(userName + " num = " + num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

2.3 執行結果(多次執行一定是這樣)

在這裡插入圖片描述
如圖說明執行緒B是等待執行緒A執行完畢後並釋放了鎖,才能獲取鎖

3.思考問題-如果是多個物件的鎖

在上面1.2.4裡只定義了一個HasPrivateNum的物件,如果有個HasPrivateNum物件,AB執行緒範圍不同的物件,會是同步還是非同步呢?

3.1 修改Run類

public class RunTwo {
    public static void main(String[] args) {
        HasPrivateNum numRef_1 = new HasPrivateNum();
        HasPrivateNum numRef_2 = new HasPrivateNum();
        
        //A
        ThreadA aThread = new ThreadA(numRef_1);
        aThread.start();

        //B
        ThreadB bThread = new ThreadB(numRef_2);
        bThread.start();
    }
}

A執行緒和B執行緒分別用不同的物件

3.2 執行結果

在這裡插入圖片描述
因為A執行緒睡了2秒,說是後列印的,這個2個執行緒是非同步執行的,不相互影響
這個結果說明關鍵字synchronized的鎖是物件鎖,不是把一段程式碼或函式鎖住

4.思考問題-非synchronized方法會鎖著嗎

一個物件有synchronized方法和非synchronized方法,如果執行緒A佔用了synchronized方法,執行緒B是否可以訪問這個物件的非synchronized方法?

4.1修改HasPrivateNum類

public class HasPrivateNum {
    private int num = 0;

    // add synchroized
    public synchronized void addNum(String userName){
        try{
            if (userName.equalsIgnoreCase("wang.dong")){
                num = 1;
                System.out.println("wang dong set over");
                Thread.sleep(2000);
            }else {
                num = 2;
                System.out.println("other man set over");
            }
            System.out.println(userName + " num = " + num);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    // not synchronized
    public void anotherMethod(){
        try{
            System.out.println(Thread.currentThread().getName() + " anotherMethod begin");

            Thread.sleep(2000);

            System.out.println(Thread.currentThread().getName() + " anotherMethod end");
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

4.2修改執行緒B呼叫非synchronized方法

public class ThreadB extends Thread{
    private HasPrivateNum num;

    public ThreadB(HasPrivateNum num){
        super();
        this.num = num;
    }

    @Override
    public void run(){
        super.run();
        //num.addNum("other man");
        num.anotherMethod();
    }
}

4.3 run方法

public class Run {
    public static void main(String[] args) {
        HasPrivateNum numRef = new HasPrivateNum();

        //A
        ThreadA aThread = new ThreadA(numRef);
        aThread.start();

        //B
        ThreadB bThread = new ThreadB(numRef);
        bThread.start();
    }
}

4.4 執行結果

在這裡插入圖片描述
從結果看出執行緒A和執行緒B是非同步執行的

  • 多個執行緒同時訪問同一個object的synchronized(this)程式碼塊時,一個時間只有一個執行緒,其他執行緒只能等待
  • 其他執行緒可以訪問該object的非synchronized(this)程式碼塊

這次暫時寫這麼多吧,加油!