簡體   English   中英

Java 同步關鍵字

[英]Java synchronized keyword

我正在測試 java 中的同步功能,但似乎我現在正在正確使用它,我希望兩個線程遞增一個整數,並在方法的簽名中使用同步關鍵字,遞增的結果應該是 200000,但我是什么getting 小於 200000,這意味着線程沒有正確同步,我的代碼:

public class Threadproblem extends Thread
{
    static long counter;
    synchronized public void run()
    {
        for (int i=0; i<100000;i++)
        {
            counter++;
        }
    }
    public static void main(String[] args) {
        Threadproblem thr=new Threadproblem();
        Threadproblem thr2=new Threadproblem();
        thr.start();
        thr2.start();
        try
        {
            thr.join();
            thr2.join();
        }
        catch(InterruptedException e)
        {
            System.out.println(e);
        }
        System.out.println("Counts:"+counter);

    }       
}

執行:

Counts:137978

正確的同步要求對受保護變量的所有訪問都在持有相同監視器的同時執行。 對於實例方法, synchronized關鍵字使方法自動獲取調用它的實例的監視器。 您有兩個單獨的Threadproblem實例,因此每個實例都使用自己的監視器。 這根本不提供同步。

在使用synchronized時解決此問題的一種方法是為您的類提供一個用於遞增計數器的同步靜態方法。 同步靜態方法使用與定義它們的類相關聯的監視器,因此您的ThreadProblem的兩個實例將使用相同的監視器:

public class Threadproblem extends Thread {
    static long counter;

    synchronized static void incrementCounter() {
        counter++;
    }

    public void run() {
        for (int i = 0; i < 100000;i++) {
            Threadproblem.incrementCounter();
        }
    }

    public static void main(String[] args) {
        Threadproblem thr = new Threadproblem();
        Threadproblem thr2 = new Threadproblem();

        thr.start();
        thr2.start();
        try {
            thr.join();
            thr2.join();
        } catch(InterruptedException e) {
            System.out.println(e);
        }
        System.out.println("Counts:" + counter);
    }       
}

或者正如@HiranChaudhuri 在評論中建議的那樣,您可以通過使兩個線程使用相同的Runnable對象來實現相同的目的,並使用屬於該對象的同步方法。 例子:

public class Threadproblem implements Runnable {
    static long counter;

    synchronized public void run() {
        for (int i = 0; i < 100000;i++) {
            counter++;
        }
    }

    public static void main(String[] args) {
        Threadproblem problem = new Threadproblem();

        // Note: instances of plain Thread:
        Thread thr = new Thread(problem);
        Thread thr2 = new Thread(problem);

        thr.start();
        thr2.start();
        try {
            thr.join();
            thr2.join();
        } catch(InterruptedException e) {
            System.out.println(e);
        }
        System.out.println("Counts:" + counter);
    }       
}

無論如何,這更好,不管同步問題如何,因為擴展Thread幾乎總是錯誤的事情。 您應該通過為其提供適當的Runnable來定義線程的工作,而不是通過覆蓋Thread本身的run()方法。

請注意,在主線程和兩個附加線程之間也存在數據競爭的可能性,但這些已經被避免,因為啟動一個線程和加入一個線程在所涉及的兩個線程之間提供了適當的排序語義。

每次“運行”的執行都在其自己的對象上同步,這意味着您根本沒有同步。

您需要在同一個對象上同步。 在您的情況下,該課程可能是合適的。 將其表示為“同步”語句,命名要同步的對象(在本例中為類文字)。

public void run() {
   synchronized (Threadproblem.class) { 
      ... 
   }
}

Java中的每個Object都有一個隱式鎖,就是允許同步和互斥的元素。 每次您在特定對象上調用非靜態函數時,都會獲取其鎖,並且在第一個線程釋放該對象之前,其他線程無法調用該對象上的動態函數。

所以你實際上應該做的是:

  • 您使用要互斥執行的方法創建一個新類
  • 您在 main 方法中創建該類的一個對象
  • 每個線程通過該對象調用同步函數。 這樣,在互斥的情況下,同一時間只有一個線程可以獲得鎖並遞增計數器。

這是一個很好的例子: https ://www.geeksforgeeks.org/object-level-lock-in-java/

// Java program to illustrate
// Object lock concept

// Class
// Extending Runnable interface
class Geek implements Runnable {

    // Method of this class
    public void run() { Lock(); }

    // Synchronization of non-static methods
    // (object lock) as different synchronized
    // non-static methods are called in both threads

    // Then both threads need to acquire the object lock
    // After one is acquired, the other thread must wait
    // for one thread to finish the executing
    // before the other thread starts to execute.
    public void Lock()
    {
        System.out.println(
            Thread.currentThread().getName());
        synchronized (this)
        {
            System.out.println(
                "in block "
                + Thread.currentThread().getName());
            System.out.println(
                "in block "
                + Thread.currentThread().getName()
                + " end");
        }
    }

    // Main driver method
    public static void main(String[] args)
    {
        // Creating an object of above class
        // in the main() method
        Geek g = new Geek();

        // Sharing the same object across two Threads

        // Here, t1 takes g
        Thread t1 = new Thread(g);
    
        // Here, t2 takes g
        Thread t2 = new Thread(g);

        // Creating another object of above class
        Geek g1 = new Geek();

        // Here, t3 takes g1
        Thread t3 = new Thread(g1);

        // setname() method is used to change
        // name of the thread
        t1.setName("t1");
        t2.setName("t2");
        t3.setName("t3");

        // start() method beginning the execution of threads
        // as JVM calls the run() method of thread
        t1.start();
        t2.start();
        t3.start();
    }
}

您可以在不需要同步的情況下完成此任務。 只要確保增量操作是原子的即可。 使用AtomicLong及其方法而不是counter++

static AtomicLong counter = new AtomicLong(0);

counter.addAndGet(1);

暫無
暫無

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

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