簡體   English   中英

同步塊如何工作?

[英]how synchronized block works?

我正在學習多線程。 我編寫了一個使用同步程序來使用兩個線程打印10和5表的程序。 雖然同步方法給了我預期的結果,但同步塊卻沒有。 我究竟做錯了什么?

public class SynchronizationDemo {

public static void main(String[] args) {
    Thread1 t=new Thread1(10);
    Thread1 t1=new Thread1(5);
    Thread thread1=new Thread(t);
    Thread thread2=new Thread(t1);
    thread1.start();
    thread2.start();
}
//synchronized method
/*public static synchronized void printTable(int num) {
    for (int i = 1; i <= 10; i++) {
        System.out.println(num*i);
        try {
            Thread.sleep(1000);
        }catch(InterruptedException ie){
            ie.printStackTrace();
        }
    }
}*/
//synchronized block
public void printTable(int num)
{
    synchronized(this){
        for (int i = 1; i <= 10; i++) {
            System.out.println(num*i);
            try {
                Thread.sleep(1000);
            }catch(InterruptedException ie){
                ie.printStackTrace();
            }
        }
     }
   }
 }

class Thread1 implements Runnable{
    int num;
    Thread1(int num){
        this.num=num;
    }
    @Override
    public void run() {
        new SynchronizationDemo().printTable(num);
    }
}

代碼輸出:10 5 10 20 30 15 20 40 25 50 60 30 70 35 40 80 90 45 100 50

預期輸出:10 20 30 40 50 60 70 80 90 100 5 10 15 20 25 30 35 40 45 50

兩種方法之間的主要區別在於小而重要的細節。

  • 您的同步塊同步在this

  • 您的同步方法(已注釋掉的方法!)是static方法。 這意味着它正在SynchronizationDemo類對象上進行SynchronizationDemo

但這並不能解釋全部。 另一件事是調用方法的方式。

    new SynchronizationDemo().printTable(num);

您正在創建一個新實例,然后在其上調用該方法。

  • 調用靜態方法時,使用哪個實例沒有區別...,因為您正在Class對象上進行同步。

  • 當您調用實例方法時,實例是不同的, 因此根本沒有相互排斥。 當兩個線程在同一對象上同步時,您只會得到互斥和正確的同步。

您在兩個不同的上下文中使用synchronized關鍵字。

  1. static synchronized方法在SynchronizationDemo類級別等待並獲得一個鎖,並且該類只有一個鎖。 因此,該類的所有實例都必須按順序等待該鎖。

  2. synchronized(this)塊(甚至一個方法)等待並獲取SynchronizationDemo類的對象鎖,每個對象都有一個鎖。 當每個run()方法創建其自己的new SynchronizationDemo()實例new SynchronizationDemo() ,該方法無需等待其他方法完成即可。

嘗試通過傳遞對象來更改Thread1類。

class Thread1 implements Runnable{
    SynchronizationDemo demo;
    int num;

    Thread1(SynchronizationDemo demo, int num){
        this.demo = demo;
        this.num = num;
    }

    @Override
    public void run() {
        demo.printTable(num);
    }
}

現在,您可以將SynchronizationDemo類的相同對象傳遞給兩個線程,如下所示。

public static void main(String[] args) {
    SynchronizationDemo demo = new SynchronizationDemo();
    Thread1 t=new Thread1(demo, 10);
    Thread1 t1=new Thread1(demo, 5);
    Thread thread1=new Thread(t);
    Thread thread2=new Thread(t1);
    thread1.start();
    thread2.start();
}

不同之處在於您還將方法更改為不再是static

public static synchronized void printTable(int num) { }

鎖定在類上,而不在任何單個實例上

等效的同步塊將是

synchronized(SynchronizationDemo.class){
}

因此,即使您有兩個實例,它們仍然使用相同的鎖(因為它們都屬於同一類),而synchronized(this)鎖定兩個獨立的對象。

您正在線程的run()方法中創建SynchronizationDemo對象的新實例。

@Override
public void run() {
    new SynchronizationDemo().printTable(num);
}

因此,每個線程都有一個新對象。 因此,在SynchronizationDemo.class中,如果兩個或多個線程無法訪問同一對象,則同步塊是無用的。 因此,線程工作於不同的對象,並且每個線程都打印出其編號。

如果thread1thread2早工作,請運行兩次或兩次以上的代碼,可以看到不同的輸出。

您應該將static synchronized關鍵字與printTable方法一起使用;

public static synchronized  void printTable(int num){
     //do sth.
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM