簡體   English   中英

為什么這些代碼塊沒有給出相同的結果?

[英]Why don't these blocks of code give the same result?

所以我是這個Thread的新手,我寫了一個簡單的程序來測試避免競爭條件。 我的第一次嘗試是使用Named Inner類:

/* App1.java */

package ehsan;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class App1{
    private final int poolSize = 10;
    private final int numLoop = 5;
    private int lastThread = 0;

    public App1() {
        ExecutorService taskList = Executors.newFixedThreadPool(poolSize);
        for (int i = 0;i < poolSize;i++) {
            taskList.execute(new Counter());
        }
        taskList.shutdown();
    }

    private class Counter implements Runnable{

        @Override
        public void run() {
            synchronized (this) {
                int currentThread = lastThread;
                System.out.println("Current thread : "+currentThread);
                lastThread = lastThread + 1;
            }
            System.out.println("Thread was executed");
        }
    }

}

App1Test.java

package ehsan;

import java.io.IOException;

public class Test {
    public static void main(String[] args) throws IOException {
        new App1();
    }
}

結果它顯示:

Current thread : 0
Thread was executed
Current thread : 1
Thread was executed
Current thread : 1
Thread was executed
Current thread : 3
Thread was executed
Current thread : 4
Thread was executed
Current thread : 5
Thread was executed
Current thread : 6
Thread was executed
Current thread : 7
Thread was executed
Current thread : 6
Current thread : 8
Thread was executed
Thread was executed

整個事情變得混亂,即使我在那里使用synchronized ,我也面臨着競爭條件。

但我的第二次嘗試有效!

package ehsan;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class App1 implements Runnable{
    private final int poolSize = 10;
    private final int numLoop = 5;
    private int lastThread = 0;

    public App1() {
        ExecutorService taskList = Executors.newFixedThreadPool(poolSize);
        for (int i = 0;i < poolSize;i++) {
            taskList.execute(this);
        }
        taskList.shutdown();
    }

    @Override
    public void run() {
        synchronized (this) {
            int currentThread = lastThread;
            System.out.println("Current thread : "+currentThread);
            lastThread = lastThread + 1;
            System.out.println("Thread was executed");
        }
    }
}

結果如我所料:

Current thread : 0
Thread was executed
Current thread : 1
Thread was executed
Current thread : 2
Thread was executed
Current thread : 3
Thread was executed
Current thread : 4
Thread was executed
Current thread : 5
Thread was executed
Current thread : 6
Thread was executed
Current thread : 7
Thread was executed
Current thread : 8
Thread was executed
Current thread : 9
Thread was executed

所以我的問題是為什么我的第一次嘗試不起作用而第二次嘗試起了很大作用? 感謝您的幫助,我是多線程編程的初學者!

在第一個程序中,您創建一個不同的Counter實例作為Runnablerun()方法由每個線程執行,因此synchronized (this)對每個線程使用不同的鎖,因此代碼不是線程安全的。 如果您使用相同的Counter實例而不是為每個線程創建一個新實例,則此程序也將按預期運行。

    Counter counter = new Counter();
    for (int i = 0;i < poolSize;i++) {
        taskList.execute(counter);
    }

在第二個程序中,您使用與Runnable相同的App1實例,其run()方法由所有線程執行,因此synchronized (this)對所有線程使用相同的鎖。

經過大量的研究,我找到了解決這個問題的方法。

請注意 :Eran,Thomas等人指出了其中一些解決方案,我感謝他們幫助我,但我只想在一個答案中收集所有可能的解決方案,以便本文的未來訪問者能夠輕松找到答案。

  • 對於命名內部類:

我們可以使用OutterClass實例,而不是將this用作同步的鎖定對象:

 synchronized (App1.this) {
        int currentThread = lastThread;
        System.out.println("Current thread : "+currentThread);
        lastThread = lastThread + 1;
 }
  • 對於分離的類:

解決方案1


同步我們從調用者類接收的對象(包含task list的類和...)。 這是一個例子:

public App1 implements Runnable {

    private final Integer shared;/* Not spending time on auto-boxing */

    public App1(Integer sharedNum) {
        shared = sharedNum;
    }

    @Override
    public void run() {
        synchronization(shared){
            //code here
        }
    }
}

public App1Test {
    private final int forSharing = 14;
    public static void main(String[] args) {
        ExecutorService taskList = Executors.newFixedThreadPool(poolSize);
        taskList.execute(new App1(forSharing));
        // and lob lob
    }
}

解決方案2


類對象的同步:

synchronized (App1.class) { /* As the fields and methods of class App1 are the same for all objects */
    int currentThread = lastThread;
    System.out.println("Current thread : "+currentThread);
    lastThread = lastThread + 1;
}

解決方案3


在靜態字段上同步(我喜歡它,因為它真的很有創意):

/* Add this field to class definition */
private static Object sharedObject = new Object();

/* Now in `run` method  use the object like this : */
synchronized(sharedObject) {
    //TODO : write your code here :)
}

所以這些是這個偷偷摸摸的問題的解決方案,有點難以調試:)

我希望這能幫助那些面臨同樣問題的人:)

干杯,Ehsan

synchronized (this) in Counter在實例上同步,所以如果你將一個新實例傳遞給每個線程( taskList.execute(new Counter()); )根本不會有任何同步(因為沒有2個線程使用相同的要同步的實例)。 因此,使用synchronized(Counter.class)或其他一些共享監視器對象(因為CounterApp1的內部類,您可以使用synchronize(App1.this) )。

您將第二種方法傳遞給每個線程,因為您將第二種方法傳遞給每個線程: taskList.execute(this);

暫無
暫無

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

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