同步方法无法按预期工作

我有一个由两个线程共享的变量。 这两个线程将对它进行一些操作。 我不知道为什么每次执行程序时sharedVar的结果都不同。

public class Main { public static int sharedVar = 0; public static void main(String[] args) { MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); mt1.start(); mt2.start(); try { // wait for the threads mt1.join(); mt2.join(); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println(sharedInt); // I expect this value to be 20000, but it's not } } 

以下是“MyThread”类

 public class MyThread extends Thread { private int times = 10000; private synchronized void addOne() { for (int i = 0; i < times; ++i) { Main.sharedVar ++; } } @Override public void run() { addOne(); } } 

sharedVar的最终结果有时是13735,12508或18793; 但从来没有20000,这是我期望的结果。 该计划的另一个有趣的事情是时间= 1000。 我总是得到2000作为最终结果。

有谁能解释这种现象?

synchronized方法保护资源, this意味着您的代码等效于:

 private void addOne() { synchronized(this) { for (int i = 0; i < times; ++i) { Main.sharedVar ++; } } } 

但是你有2个对象,调用addOne方法。 这意味着mt1.addOne thismt2.addOne ,因此你没有共同的公共资源。

尝试将addOne代码更改为:

 private void addOne() { synchronized(MyThread.class) { for (int i = 0; i < times; ++i) { Main.sharedVar ++; } } } 

你会观察到预期的行为。 如下面的评论所示,最好使用与MyThread.class不同的对象进行同步,因为类对象可以从许多点访问,而其他代码很容易尝试使用同一对象进行同步。

在非静态方法上使用synchronized时,使用当前对象作为监视器。

使用synchronized on static方法时,使用类的当前对象( ClassName.class静态字段)作为监视器。

在您的情况下,您在Thread的对象(2个不同的实例)上使用synchronized ,因此两个不同的线程将同时修改您的sharedVar静态字段。

您可以通过不同方式修复它。

addOne方法移动到Main并使其成为static

 private static synchronized void addOne(int times) { for (int i = 0; i < times; ++i) { sharedVar++; } } 

或者您可以使用字段private int var;创建名为SharedVarprivate int var; 和方法synchronized void addOne(int times)并将SharedVar单个实例SharedVar给您的踏板。

 public static void main(String[] args) { SharedVar var = new SharedVar(); MyThread mt1 = new MyThread(var); MyThread mt2 = new MyThread(var); mt1.start(); mt2.start(); try { // wait for the threads mt1.join(); mt2.join(); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println(var.getVar()); // I expect this value to be 20000, but it's not } 

但是如果只需要在多个线程中更改一个整数,则可以使用java.til.concurrent.*类,如AtomicLongAtomicInteger

sharedVar定义为AtomicLong而不是int 。 使函数synchronized起作用,但效率较低,因为您只需要同步增量。

当线程即将执行’ synchronized实例方法时 ,它会对Object上的锁定(确切地说,锁定该对象监视器)。

所以在你的情况下,线程mt1获取对象mt1上的锁定,并且线程mt2获取对象mt2的锁定,并且它们不会阻塞彼此,因为两个线程正在处理两个不同的锁定。

当两个线程同时修改共享变量(非同步方式)时,结果是不可预测的。

关于值1000的情况,对于较小的输入,交错执行可能导致正确的结果(幸运的是)。

Sol:从addOne方法中删除synchronized关键字,并将sharedVal作为’ AtomicInteger ‘的类型

start方法后立即加入线程。 从这个线程1开始并在线程2开始并进入死状态后进入死状态。 因此它将始终打印您的预期输出。

更改代码如下所示: –

 public class Main{ public static int sharedVar = 0; public static void main(String[] args) { MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); try { mt1.start(); mt1.join(); mt2.start(); mt2.join(); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println(sharedVar); } } class MyThread extends Thread { private int times = 1000000; private synchronized void addOne() { for (int i = 0; i < times; ++i) { Main.sharedVar++; } } @Override public void run() { addOne(); } }