【问题标题】:Not able to synchronize process using Thread无法使用线程同步进程
【发布时间】:2018-01-03 07:55:16
【问题描述】:

我正在通过Thread 的行为测试我的技能。当我实现Runnable 接口和synchronized 运行方法时,我得到了绝对的结果。但是,当我扩展 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。谁能解释一下。。我要疯了!!

【问题讨论】:

  • 问问自己:第一种情况下synchronized(this)中的this是什么,第二种情况下是什么。
  • 好的等等,问..我的意思是想..
  • 因为 .. 你不是在柜台上同步,而是在其他有多个实例的东西上同步?
  • 你说得对。 this 是 One 的实例。在第一种情况和第二种情况下,您创建了多少个One实例?
  • 不。那不是问题。确实有两个 One 实例,但都共享同一个计数器。问题是第一个线程在 One 的第一个实例上同步,第二个线程在 One 的第二个实例上同步。所以他们访问一个共享资源(计数器),但在不同的锁上同步。所以在第一个线程中进入同步块并不会阻止另一个线程进入它的同步块。

标签: java multithreading synchronization


【解决方案1】:

在此示例中,我将仅在计数器实例上进行同步,而不是按照其他人的建议将同步放在 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());
    }

}

【讨论】:

  • 既然我已经理解了我的疑惑,这似乎也是一种很酷且精确的方式
【解决方案2】:

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()

【讨论】:

  • 同步 doCount() onluy 不会使 Counter 线程安全。 getCount() 也必须在同一个锁上同步。
  • @JBNizet 你是对的;我没有发布完整的代码
  • 这就是为什么我非常喜欢堆栈溢出社区,因为他们采用了开箱即用的方法来解决问题。
  • 为什么要引入锁对象,当两个方法上的同步关键字也可以工作时?
  • @mlecz 我提到同步(计数器)也可以工作。我只是更喜欢第二个
【解决方案3】:

在第二种情况下,底层“this”对象指的是 One 类的两个不同实例,即 o1 和 o2。 for 循环独立运行,在两个不同的实例上具有对象监视器锁定,因此显然此处的计数器修改未同步。

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

【讨论】:

    【解决方案4】:

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

    万一你创建了

    One one = new One();
    

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

    如果您创建了两个对象,并且两者都使用不同的对象作为锁。您可以使用计数器作为锁来代替这个,这将解决您的问题。

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

    【讨论】:

      【解决方案5】:

      好的,首先你应该知道实现runnable和从线程here继承之间的区别。 然后,阅读this清楚地了解添加同步的内容。

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

      【讨论】:

      • @JSK 很高兴你喜欢它们。
      猜你喜欢
      • 1970-01-01
      • 2018-03-10
      • 2011-11-03
      • 1970-01-01
      • 2011-04-15
      • 2010-12-18
      • 1970-01-01
      • 2010-10-03
      相关资源
      最近更新 更多