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

时间:2019-02-18 18:43:00

标签: java multithreading java-threads atomicinteger

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

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

我的主要课程如下

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

代替以前的预期结果。但是,当我将整数更改为原子整数类型时,我开始获得预期的结果。谁能解释我该怎么做才能使它以整数而不是原子整数运行

4 个答案:

答案 0 :(得分:1)

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

另外,要获得正确的结果,可以将num变量更改为static变量。

public static Integer num = 1;

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

答案 1 :(得分:0)

您的问题是Integer类为Immutable,因此您不能在单独的线程中使用它来引用共享值。答:创建自己的Mutable Integer类。

您可以在SO here上找到类似的答案

答案 2 :(得分:0)

仅就您所知,您可能不希望在synchronized上使用Object(例如Lock),而在ReentrantLock上使用Condition块,并且他们关联的Condition

使用Public Function OleDragEnter(<[In]> <MarshalAs(UnmanagedType.Interface)> pDataObj As Object, <[In]> <MarshalAs(UnmanagedType.U4)> grfKeyState As Integer, <[In]> <MarshalAs(UnmanagedType.U8)> pt As Long, <[In]> <Out> ByRef pdwEffect As Integer) As Integer Implements IOleDropTarget.OleDragEnter Dim x As Integer = CInt(pt And &H7FFFFFFF) Dim y As Integer = CInt((pt >> 32) And &H7FFFFFFF) Dim winPT As Win32Point winPT.x = CInt(pt And &H7FFFFFFF) winPT.y = CInt((pt >> 32) And &H7FFFFFFF) ddHelper.DragEnter(hwnd, CType(pDataObj, NativeMethods.IDataObject), winPT, 0) End Function (s),可以在线程之间以互斥的方式管理共享资源。

答案 3 :(得分:0)

我仍然认为此问题未得到正确回答。此处的缺陷是您从未将共享数据标记为static。因此,每个线程都有自己的副本,彼此独立。 Integer是一个不变的包装器类,这是对的,但是在这种情况下它无关紧要。让我们深入研究num++++运算符仅适用于(原始)整数类型。在幕后,将num取消装箱,应用++,然后将结果分配回num(在装箱转换之后)。 Integer类没有++运算符。实际上,Integer对象是不可变的。

Immutable表示每次递增并创建新的值对象时。并将该新值对象分配回您的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();
    }
}

最后使用客户端提供的锁定对象会违反同步策略的封装。因此,我改用了内部私有锁对象。

这是新的输出。

  

第一1第一2第二3第二4第一5第一6第二7   第二8第一9第一10