Java同步块意外行为
我有以下代码:Java同步块意外行为,java,multithreading,synchronized,Java,Multithreading,Synchronized,我有以下代码: public class Experimenter implements Runnable { private volatile Integer a = new Integer(0); public Experimenter() throws Exception { System.out.println("start"); } public void funk() throws InterruptedException
public class Experimenter implements Runnable
{
private volatile Integer a = new Integer(0);
public Experimenter() throws Exception
{
System.out.println("start");
}
public void funk() throws InterruptedException
{
synchronized (a)
{
System.out.println("in");
Thread.sleep(5000);
System.out.println("out");
}
}
public static void main(String[] args) throws Exception
{
Thread a = new Thread(new Experimenter(), "a");
Thread b = new Thread(new Experimenter(), "b");
Thread c = new Thread(new Experimenter(), "c");
Thread d = new Thread(new Experimenter(), "d");
Thread e = new Thread(new Experimenter(), "e");
a.start();
b.start();
c.start();
d.start();
e.start();
}
@Override
public void run()
{
try
{
funk();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
我曾预计,由于一次只能有一个线程使用同步块,它将打印如下内容,每个输入和输出之间有5秒的间隔:
start
start
start
start
start
in
out
in
out
in
out
in
out
in
out
但是,我得到了以下信息。所有输入,5秒后,所有输出:
start
start
start
start
start
in
in
in
in
in
out
out
out
out
out
有人能解释一下吗?每个线程都在自己的锁上同步,因为每个线程都使用自己的实验者实例,而每个实验者实例都有自己的整数实例作为锁。如果希望线程同步,则需要在所有实例之间共享唯一的锁。顺便说一句,您不应该使用整数。请使用简单对象:
final Object sharedLock = new Object();
Thread a = new Thread(new Experimenter(sharedLock), "a");
Thread b = new Thread(new Experimenter(sharedLock), "b");
...
最后一个关键字的使用:
这不是严格需要的。但是锁,特别是当它们被声明为字段时,应该是最终的,以确保您不会意外地为变量分配新值,这将导致线程使用两个不同的锁对象,非常简单:您的
a
不会在任何实验者之间共享;它是一个实例变量,每个实验者一个。顺便说一句,volatile
在这种情况下几乎没有用处
如果需要共享锁,请将其设为私有静态final
变量。在这种情况下也不需要使用volatile
但我会选择@JBNizet的解决方案,它更干净
编辑:为什么最终版
?因为它保证在初始化后不会更改;但是final
变量最重要的方面来自Java内存模型,它指出final
变量的初始化发生在读取该变量之前。这是一条非常有力的规则。这肯定会对你有用
public void funk() throws InterruptedException
{
synchronized (Experimenter.class)
{
System.out.println("in");
Thread.sleep(5000);
System.out.println("out");
}
}
将字段a
声明为static
,它将按预期工作。在代码中,该字段是实例成员,因此实际上有五个不同的监视器 你能解释一下“最终版”的必要性吗?它不是严格需要的。但是锁,特别是当它们被声明为字段时,应该是最终的,以确保不会意外地为变量分配新值,这将导致线程使用两个不同的锁对象。请您将此添加到您的答案中,以便更好地帮助未来用户?您能解释一下“final”的必要性吗?然后他必须使用类级锁???@Prashant不,基本上他不需要;但无论使用哪个锁,它都必须在所有线程之间共享。一个private static final
变量是一个解决方案,还有其他解决方案,比如@JBNizet(我认为它更干净),如果您使用的是类级锁,那么变量应该是静态的,就像其他一些解决方案一样!!!