簡體   English   中英

為什么我們需要一個靜態鎖來同步system.out.println()?

[英]Why do we need a static lock for synchronizing system.out.println()?

我正在學習Java認證,並且可以從Mughal的書中看到以下示例:

public class Smiley extends Thread
{
    @Override
    public void run()
    { 
        while(true)
        { 
            synchronized(this)
            {
                try
                { 
                    System.out.print(":"); 
                    Thread.sleep(100); 
                    System.out.print("-"); 
                    Thread.sleep(100); 
                    System.out.println(")"); 
                    Thread.sleep(100); 
                }
                catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }


    public static void main(String[] args)
    {
        new Smiley().start();
        new Smiley().start();
    }
}

目的是每行打印一個笑臉:-)。 我的問題是,為什么在實例(this)上進行同步無法實現這一目標? 為什么我們需要在靜態級別進行同步?

謝謝,

因為請注意main()函數創建兩個 Smiley類。 它們各自在各自的線程上運行。 由於他們都this進行了鎖定, this他們倆都將立即獲得該鎖定,而不會與另一個線程爭用。 在這種情況下,他們的synchronize(this)鎖定方案什么都不做。

處理多線程問題時,您必須考慮“我要保護什么?” 在這種情況下,您需要保護System.out ,以確保按所需順序訪問它。 由於System.out是靜態的,因此您需要某種外部作用域鎖, 每個線程在寫入之前必須先獲取它們。

您可以使用ReentrantLock實現此目的。

請不要使用同步的(this)-一般而言,這是錯誤的做法
如上所述,鎖應該在兩個線程之間共享,在這種情況下,鎖是每個類的實例(即,由新Smiley創建的對象)。
您應該擁有一個共享鎖,也許可以通過使用靜態變量來實現,該變量在同一類的所有實例之間共享,
或將鎖作為參數傳遞給笑臉的CTOR。
我將根據@Jonathon Reinhart的建議使用第二個選項作為示例,以使用可重入鎖

public class Smiley extends Thread
{
   private  ReentantLock lock;

   public Smiley(ReentrantLock lock) { 
      this.lock = lock;
   }

   @Override
   public void run()
   { 
     while(true)
     { 
        try {
           lock.lock();
           System.out.print(":"); 
           Thread.sleep(100); 
           System.out.print("-"); 
           Thread.sleep(100); 
           System.out.println(")"); 
           Thread.sleep(100); 
        }
        catch(InterruptedException e) {
                e.printStackTrace();
        }
        finally {  
           lock.unlock();
        }
     } 
  }

}


public static void main(String[] args)
{
    ReentrantLock lock = new ReentantLock();
    new Smiley(lock).start();
    new Smiley(lock).start();
}

一些指針-
一種。 切記解鎖代碼必須位於finally子句中-這是一個好習慣(您也可以使用try塊和finally塊,而不使用catch塊)。
b。 您可以根據需要考慮用java.util.concurrent包中的其他鎖替換ReentrantLock

您實際上要問兩個問題,答案是:

  • 為什么在實例(this)上進行同步無法實現這一目標?

因為您要獲取兩個不同的隱式鎖,所以同步塊內部的指令允許兩個線程同時執行,並且實際上可以被交錯。

  • 為什么我們需要在靜態級別進行同步?

您無需在靜態級別進行同步。 您需要在線程共享的對象的同一實例上進行同步。

實現所需目標的最簡單方法是通過以下方式在System.out上進行同步:

@Override
public void run() {
    while (true) {
        synchronized (System.out) {
            try {
                System.out.print(":");
                Thread.sleep(100);
                System.out.print("-");
                Thread.sleep(100);
                System.out.println(")");
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

暫無
暫無

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

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