假设此演示中使用了两个线程 increment()
代码块首先执行并获取当前对象上的监视器。其他线程是否不能执行该方法 decrement()
? .
有人能帮我理解吗?
如果我运行了这个应用程序,其他线程就可以执行非同步方法,即使它锁定了休眠线程持有的对象 10000 ms
.
package com.learn.threads;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadDemo {
int sharedVariable;
public ThreadDemo(int sharedVariable) {
this.sharedVariable = sharedVariable;
}
public synchronized void increment() throws InterruptedException {
Thread.sleep(10000);
this.sharedVariable++;
}
public void decrement() throws InterruptedException {
this.sharedVariable--;
}
public static void main(String[] args) throws InterruptedException {
ThreadDemo task = new ThreadDemo(0);
ExecutorService incrementExecutorService = Executors.newFixedThreadPool(2);
for (int i = 0; i < 6; i++) {
incrementExecutorService.submit(() -> {
try {
task.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread.sleep(5000);
incrementExecutorService.submit(() -> {
try {
task.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
Thread.sleep(35000);
incrementExecutorService.shutdown();
System.out.println(task.sharedVariable);
}
}
1条答案
按热度按时间3hvapo4f1#
不,不会的。
synchronized
在一个方法上,语法只是将整个方法体 Package 在synchronized (X) {}
,其中x是this
例如方法和YourClass.class
对于静态的。这是一个严重的设计错误,除非你记录了你的类的锁定行为,如果你这样做的话——任何时候你锁定了其他代码可以引用的东西(this
以及YourClass.class
通常是公开的),您需要对此进行记录,并努力支持您在未来版本中记录的内容。synchronized
与同一引用上的其他同步块交互,以及thatRef.wait/notify/notifyAll()
没有别的了。它本身没有任何影响,总是需要两个不同的线程同时命中一个同步块,在同一个对象上同步,否则就没有任何用处。粘贴的代码段已损坏:如果某些线程调用
decrement()
,其他线程可能会也可能不会观察到这一点,因为没有建立cbca关系。任何读取sharedVariable
需要锁定ThreadDemo
,和decrement
方法需要获得synchronized
属性。请注意,具有可递增/可递减对象的任务已经存在:
AtomicInteger
,如果这是您的实际意图,那么您应该使用它(但我假设您编写这篇文章只是作为一个示例)。注:java内存模型最好理解为一枚邪恶的硬币。邪恶的是,它会给你带来麻烦:让代码在每次测试和所有测试中都能很好地工作,第一周你就把它放到生产服务器上,然后就在那个重要的客户得到一个演示时,它崩溃了。您必须编写这样的代码,使得vm永远不会翻转硬币(或者更确切地说,翻转的结果不会影响您的代码),并且没有简单的方法可以知道邪恶的硬币正在被翻转。穿线很难正确,是的。为什么您认为现实世界中的大多数多线程代码都是通过消息总线或事务数据库进行线程间通信的?任何代码在任何地方触及任何字段时,硬币都会被翻转,硬币的结果决定线程是使用该字段的本地克隆副本,还是从共享副本读取。因此,
sharedVariable--
可能会导致减少,只有你的线程可以看到,或所有线程可以看到,这取决于邪恶硬币的结果。如果代码所做的事情取决于翻转,那么您就搞砸了,测试也无法捕捉到它。你可以通过建立一种先来后到的关系来避免投币sharedVariable--
不管鳕鱼读到什么sharedVariable
.synchronized
是建立这种关系的几种方法之一。在web上搜索“java内存模型”“synchronized”以获取更多信息—但请注意,这是非常复杂的事情。