为什么这些代码块没有给出相同的结果?

时间:2016-07-06 08:53:36

标签: java multithreading

所以我是这个Thread的新手,我写了一个简单的程序来测试避免竞争条件。我的第一次尝试是命名内部课程:

/* 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

所以我的问题是为什么我的第一次尝试不起作用而第二次尝试起了很大作用?感谢您的帮助,我是多线程编程的初学者!

3 个答案:

答案 0 :(得分:1)

在第一个程序中,您创建了一个不同的Counter实例作为每个线程执行Runnable方法的run(),因此synchronized (this)为每个线程使用不同的锁线程,因此代码不是线程安全的。如果您使用相同的Counter实例而不是为每个线程创建一个新实例,则此程序也将按预期运行。

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

在第二个程序中,您使用与App1实例相同的Runnable实例,其run()方法由所有线程执行,因此synchronized (this)使用相同的锁定所有线程。

答案 1 :(得分:0)

通过大量研究,我找到了解决这个问题的方法。

请注意:Eran,Thomas等人指出了其中一些解决方案,我感谢他们帮助我,但我只想在一个答案中收集所有可能的解决方案,以便这篇文章的未来访客很容易找到答案。

  • 对于命名内部类:

我们可以使用OutterClass实例,而不是使用this作为锁定对象进行同步:

 synchronized (App1.this) {
        int currentThread = lastThread;
        System.out.println("Current thread : "+currentThread);
        lastThread = lastThread + 1;
 }
  • 对于分离的类:

解决方案1 ​​

同步我们从Caller Class接收的对象(包含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

答案 2 :(得分:-1)

synchronized (this)中的{p> Counter在实例上进行同步,因此如果将新实例传递给每个线程(taskList.execute(new Counter());),则根本不会有任何同步(因为没有2个线程)使用相同的实例进行同步)。因此,使用synchronized(Counter.class)或其他一些共享监视器对象(因为CounterApp1的内部类,您可以使用synchronize(App1.this))。

您将第二种方法传递给每个线程:taskList.execute(this);