具有共享整数对象的线程未按预期工作

Threads with shared integer object not working as expected

我有一个问题,我必须以这种格式打印数字。

First  1
First  2
Second  3
Second  4
First  5
First  6
Second  7
Second  8
First  9
and so on...

我已经实现了我的 运行 可用接口,如下所示。

class ThreadDemo implements Runnable {

 public volatile Integer num;

 public Object lock;

 public ThreadDemo(Integer num, Object lock) {
  this.num = num;
  this.lock = lock;
 }

 @Override
 public void run() {

  try {
   while (true) {
    int count = 0;
    synchronized(lock) {
     Thread.sleep(100);
     while (count < 2) {
      System.out.println(Thread.currentThread().getName() + "  " + num++);
      count++;

     }
     lock.notify();
     lock.wait();
    }
   }
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
}

我的主要class如下

public class CoWorkingThreads {
 private static volatile Integer num = new Integer(1);
 public static void main(String...args) {
  Object lock = new Object();
  Thread thread1 = new Thread(new ThreadDemo(num, lock), "First");
  thread1.start();
  Thread thread2 = new Thread(new ThreadDemo(num, lock), "Second");
  thread2.start();

 }
}

当我 运行 程序时,我得到的输出如下

First  1
First  2
Second  1
Second  2
First  3
First  4
Second  3
Second  4

而不是之前预期的结果。但是当我将整数更改为原子整数类型时,我开始得到预期的结果。任何人都可以解释我可以做些什么来使它 运行 与整数而不是使用原子整数

您的问题是整数 class 是 Immutable,因此您不能在单独的线程中使用它来引用共享值。答案:创建您自己的可变整数 class.

您可以在 SO here

上找到类似问题的答案

Java 不能通过引用传递整数。在您的代码中,每个线程都会创建该变量的副本。但是 atomicInteger 可以通过引用传递。

另外,为了获得正确的结果,您可以将 num 变量更改为静态变量。

public static Integer num = 1;

public Object lock;
public ThreadDemo(Integer num, Object lock) {
    //this.num = num;
    this.lock =lock;
}

仅供参考,您可能想在 Object 上尝试使用 Lock(s)(例如 ReentrantLock,而不是使用 synchronized 块) 及其关联的 Condition(s).

使用 Condition(s),您可以在线程之间以互斥方式管理您的共享资源。

我仍然认为这个问题没有得到正确回答。这里的缺陷是您从未将共享数据标记为 static。所以每个线程都有自己的独立副本。 Integer 是一个不可变的包装器 class,这是正确的,但在这种情况下它没有任何作用。让我们更深入地了解 num++++ 运算符仅适用于(原始)整数类型。在幕后,num 被取消装箱,应用 ++,然后将结果分配回 num(装箱转换后)。 Integer class 没有 ++ 运算符。事实上,Integer 对象是不可变的。

不可变意味着每次递增并创建一个新的值对象。并且该新值对象被分配回您的 num 引用。但是两个线程有​​自己的 num 引用副本,指向不同的 Integer 盒装原语。因此,它们彼此独立地增加它,而彼此不可见。如果你想在线程之间共享它,你必须在声明的地方使用 static 访问修饰符。更重要的是将两个值传递给共享变量没有意义。相反,您可以内联初始化它。这是固定版本。

public class ThreadDemo implements Runnable {
    public static Integer num = 1;

    public static final Object lock = new Object();

    public ThreadDemo() {
    }

    @Override
    public void run() {

        try {
            while (true) {
                int count = 0;
                synchronized (lock) {
                    Thread.sleep(100);
                    while (count < 2) {
                        System.out.println(Thread.currentThread().getName() + "  " + num++);
                        count++;

                    }
                    lock.notify();
                    lock.wait();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public class CoWorkingThreads {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new ThreadDemo(), "First");
        thread1.start();
        Thread thread2 = new Thread(new ThreadDemo(), "Second");
        thread2.start();
    }
}

最终使用客户端提供的锁对象违反了同步策略的封装。所以我改用了内部私有锁对象。

这是新的输出。

First 1 First 2 Second 3 Second 4 First 5 First 6 Second 7 Second 8 First 9 First 10