为什么当我的类中的计数器更大时,java线程的行为会有所不同?

时间:2014-07-03 19:34:33

标签: java multithreading

这是我的代码:

public class MyRunnableClass implements Runnable {

    static int x = 25;
    int y = 0;
    private static final Object sharedLock = new Object();


    @Override
    public void run() {
        while(x>0){
            someMethod();
        }
    }

    public synchronized void someMethod(){
        synchronized (sharedLock){
            x--;
            y++;
        }
    }
}

和测试类:

public class MyRunnableClassTest {

    public static void main(String[] args) throws InterruptedException {

        MyRunnableClass aa = new MyRunnableClass();
        MyRunnableClass bb = new MyRunnableClass();

        Thread a = new Thread(aa);
        Thread b = new Thread(bb);

        a.start();
        b.start();

        a.join();
        b.join();

        System.out.println(aa.y + bb.y);

    }
}

当我运行此代码时,我看到输出25很好,但当x为250时,我看到251 ..为什么?为什么不250?

5 个答案:

答案 0 :(得分:3)

您必须扩展synchronized范围,以便涵盖x上的读取操作:

@Override
public void run() {
    for (;;) {
        synchronized (sharedObject) {
            if (x <= 0) break;
            someMethod();
        }
    }
}

答案 1 :(得分:2)

重合。 25可能会发生同样的事情,就像任何其他数字一样。

例如,在执行

期间
while(x>0){
    someMethod();
}

没有同步,在一堆循环之后,让x为1.第一个线程开始迭代(进入正文),然后线程切换,第二个线程看到x是1,所以进入循环体。两者都会递增计数,它们的总和将等于原始x值的一个。

这是一种竞争条件,你恰好可以通过更大的数字更容易看到后果。

答案 2 :(得分:1)

当你这样做时:

    while(x>0){
        someMethod();
    }

让我们说x = 1和:

线程A评估x&gt; 0到true,进入循环。 让我们说线程A在下一行执行之前被中断。 线程B也将评估x​​> 1。 0到true并进入循环。

两者都将一个接一个递减x并递增y。

要解决此问题,请检查x&gt; 0也必须在锁中。

例如:

public class MyRunnableClass implements Runnable {

static int x = 25;
int y = 0;
private static final Object sharedLock = new Object();


@Override
public void run() {
    while(x>0){
        someMethod();
    }
}

public synchronized void someMethod(){
    synchronized (sharedLock){
        if(x > 0){
            x--;
            y++;
       }
    }
}

}

答案 3 :(得分:1)

有时,线程a和线程b都可以调用someMethod(),因为x是1.一个线程锁定sharedLock,使x等于0,y等于250,然后释放sharedLock,此时其他线程调用someMethod()并使y等于251,x等于-1。

答案 4 :(得分:0)

您也可以使用AtomicInteger解决该问题:

import java.util.concurrent.atomic.AtomicInteger;

public class MyRunnableClass implements Runnable {

    private static final AtomicInteger xHolder = new AtomicInteger(25);
    int y = 0;

    @Override
    public void run() {
        while (xHolder.decrementAndGet() >= 0) {
            y++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyRunnableClass aa = new MyRunnableClass();
        MyRunnableClass bb = new MyRunnableClass();

        Thread a = new Thread(aa);
        Thread b = new Thread(bb);

        a.start();
        b.start();

        a.join();
        b.join();

        System.out.println(aa.y + bb.y);
    }
}

或者使用一些扩展的并行性进行测试:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicInteger;

public class MyRunnableClass implements Callable<Integer> {

    private static final AtomicInteger xHolder = new AtomicInteger(250000);
    int y = 0;

    @Override
    public Integer call() throws Exception {                
        while (xHolder.decrementAndGet() >= 0) {
            y++;
        }
        System.out.println(Thread.currentThread().getName() + " returns " + y);
        return y;
    }

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        ExecutorCompletionService<Integer> completionService = new ExecutorCompletionService<Integer>(
                executorService);

        int parallelism = 5;

        for (int i = 0; i < parallelism; ++i) {
            completionService.submit(new MyRunnableClass());
        } // for

        int ySum = 0;       
        for (int j = 0; j < parallelism; ++j) {
            Future<Integer> future = completionService.take();          
            ySum += future.get();
        } // for    

        System.out.println(ySum);
        executorService.shutdown();
    }

}

输出:

pool-1-thread-3 returns 26619
pool-1-thread-5 returns 0
pool-1-thread-1 returns 104302
pool-1-thread-2 returns 95981
pool-1-thread-4 returns 23098
250000