无法使用Thread同步进程

时间:2018-01-03 07:55:16

标签: java multithreading synchronization

我正在测试我的技能而不是Thread行为。当我实现Runnable接口和synchronized run方法时,我得到了绝对的结果。但是,当我延长Thread课程时,结果是不可预测的。以下是两个案例。我认为,两种情况下的线程都使用相同的资源。

案例1 Runnable

class Counter{
    int count;
    public void doCount(){
        count=count+1;
    }
    public int getCount(){
        return count;
    }
}
public class One implements Runnable{
    Counter counter = new Counter(); // common object to be shared with two threads
    public void run(){
        synchronized (this) {
            for(int i=0;i<10000;i++){
                counter.doCount();
            }   
        }
    }
    public static void main(String[] args) throws InterruptedException {
        One one = new One();
        Thread t1 = new Thread(one);// using same resource 'one'
        Thread t2 = new Thread(one);// using same resource 'one'
        t1.start();
        t2.start();
        Thread.sleep(2000); // to give both threads time to complete
        System.out.println(one.counter.getCount());
    }
}

案例2 Thread

class Counter{
    int count;
    public void doCount(){
        count=count+1;
    }
    public int getCount(){
        return count;
    }
}
public class One extends Thread{
    Counter counter; //common object to be shared with two threads
    One(Counter counter){
        this.counter = counter; 
    }
    public void run(){
        synchronized (this) {
            for(int i=0;i<10000;i++){
                counter.doCount();
            }   
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        One o1 = new One(counter);// using same resource counter
        One o2 = new One(counter);// using same resource counter
        o1.start();
        o2.start();
        Thread.sleep(2000); // to give both threads time to complete
        System.out.println(counter.getCount());
    }
}

对于案例1,我每次输出20000。但对于案例2,我每次都会得到随机值。为什么会这样?案例2也在两个线程中使用相同的资源,那么为什么它们停止获取synchronized。任何人都可以解释这个......我疯了!

5 个答案:

答案 0 :(得分:2)

在这个例子中,我只是在计数器实例上进行同步,而不是按照其他人的建议将同步放在Counter类中。这使Counter类变得简单并且不知道任何潜在的线程问题,这些问题只有在多个线程使用该类时才会出现。

所以代码是:

public class Counter {
    int count;
    public void doCount() {
        count=count+1;
    }
    public int getCount() {
        return count;
    }
}

public class CountRunner implements Runnable {
    Counter counter; 
    public CountRunner(Counter counter){
        this.counter = counter; 
    }
    public void run() {
        synchronized (counter) {
            for(int i=0;i<10000;i++){
                counter.doCount();
            }   
        }
    }
}

public class CountThread extends Thread {
    Counter counter; 
    public CountThread(Counter counter){
        this.counter = counter; 
    }
    public void run() {
        synchronized (counter) {
            for(int i=0;i<10000;i++){
                counter.doCount();
            }   
        }
    }
}

public class App {
    public static void main(String[] args) throws InterruptedException {
        countRunnerTest();
        countThreadTest();
    }

    public static void countRunnerTest() {
        Counter counter = new Counter();
        CountRunner countRunner = new CountRunner(counter);
        Thread t1 = new Thread(countRunner);
        Thread t2 = new Thread(countRunner);
        t1.start();
        t2.start();
        Thread.sleep(2000); 
        System.out.printf("CountRunnerTest result=%s", counter.getCount());
    }

    public static void countThreadTest() {
        Counter counter = new Counter();
        CountThread t1 = new CountThread(counter);
        CountThread t2 = new CountThread(counter);
        t1.start();
        t2.start();
        Thread.sleep(2000);
        System.out.printf("CountThread  result=%s", counter.getCount());
    }

}

答案 1 :(得分:1)

synchronized (this)有问题。在这种情况下,您有2个实例:o1和02. synchronized (counter)应该可以工作,但不是理想的解决方案。

理想情况下,只是为了测试,我会使用私人锁。

class Counter{
    final Object lock= new Object();
    int count;
    public void doCount(){
        synchronized (lock){
            count=count+1;
        }
    }
    public int getCount(){
        synchronized (lock) {
            return count;
        }
    }
}

使用sleep

比使用Thread.join()更好的方法

答案 2 :(得分:1)

在第二种情况下,基础“this”对象指的是One类的两个不同实例,即o1和o2。 for循环独立运行,在两个不同的实例上有对象监视器锁,因此很明显这里的计数器修改是非同步的。

通常,标准数据结构会在这种情况下抛出ConcurrentModificationException。

答案 3 :(得分:1)

当您使用synchronized (this)时,它会锁定您调用方法的对象。

在案例一中你创建

One one = new One();

并传递给每个方法,因此两者都使用相同的对象作为锁。

在案例2中,您创建了两个对象,并且两个对象都使用不同的对象作为锁。您可以使用计数器作为锁而不是此,这将解决您的问题。

 One o1 = new One(counter);// using same resource counter
 One o2 = new One(counter);

答案 4 :(得分:1)

好的,首先你应该知道实现runnable和从线程here继承之间的区别。 然后,阅读this以了解明确添加同步的行为。

无论如何,请检查同步功能中的this。在第一种情况下,它与One one = new One(One)相关......而第二种One one = new One(One)与自身相关。两者都是同步的,但第一个与第一个实例同步。但是,第二个与第二个实例同步。尝试使用锁它应该可以工作。