我有一个由两个线程共享的变量。这两个线程将对它进行一些操作。我不知道为什么每次执行程序时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作为最终结果。
任何人都可以解释这种现象吗?
答案 0 :(得分:5)
同步方法保护资源this
,这意味着您的代码等同于:
private void addOne()
{
synchronized(this)
{
for (int i = 0; i < times; ++i)
{
Main.sharedVar ++;
}
}
}
但是您有2个对象,其中调用了addOne
方法。这意味着this
的{{1}}与mt1.addOne
的{{1}}不同,因此您没有共同的公共资源。
尝试将this
代码更改为:
mt2.addOne
您将观察到预期的行为。如下面的评论所示,最好使用与addOne
不同的对象进行同步,因为类对象可以从许多点访问,而其他代码很容易尝试使用同一对象进行同步。
答案 1 :(得分:3)
在非静态方法上使用synchronized
时,可以使用当前对象作为监视器。
在静态方法上使用synchronized
时,使用类(ClassName.class
静态字段)的当前对象作为监视器。
在您的情况下,您在Thread的对象(2个不同的实例)上使用synchronized
,因此两个不同的线程将同时修改您的sharedVar
静态字段。
你可以用不同的方式解决它。
将addOne
方法移至Main
并将其设为static
。
private static synchronized void addOne(int times)
{
for (int i = 0; i < times; ++i)
{
sharedVar++;
}
}
或者您可以使用字段SharedVar
和方法private int var;
创建名为synchronized void addOne(int times)
的班级,并将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.*
中的类,如AtomicLong
或AtomicInteger
。
答案 2 :(得分:0)
将sharedVar
定义为AtomicLong
而不是int
。使函数synchronized
也可以正常工作,但效率较低,因为您只需要同步增量。
答案 3 :(得分:0)
线程即将执行&#39; 同步&#39; 实例方法,它确定了对象的锁定(确切地说,锁定了该对象监视器)。
所以在你的情况下,线程mt1获取对象mt1上的锁定,而线程mt2获取对象mt2上的锁定,并且它们不会阻塞彼此,因为两个线程正在处理两个不同的锁。
当两个线程同时修改共享变量(非同步方式)时,结果是不可预测的。
关于值1000的情况,对于较小的输入,交错执行可能导致正确的结果(幸运的是)。
Sol:从addOne方法中删除synchronized关键字,并将sharedVal作为&#39; AtomicInteger &#39;
的类型答案 4 :(得分:0)
启动方法后立即加入线程。从这个线程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();
}
}