Java 线程:对象同时具有同步和非同步方法
Java Thread : object has both synchronized and not synchronized methods
假设在这个demo.Suppose中使用了2个线程increment()
代码块首先执行并获取当前的监视器object.does其他线程将无法执行该方法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);
}
}
不,不会。
方法上的 synchronized
只是语法糖,用于将方法的整个主体包装在 synchronized (X) {}
中,其中 X 是 this
实例方法和 YourClass.class
对于静态的。这是一个严重的设计错误,除非你记录了 class 的锁定行为,如果你这样做 - 任何时候你锁定其他代码可以引用的东西(this
和 YourClass.class
通常是public),您需要对此进行记录,并努力在未来的版本中支持您记录的内容。
synchronized
在同一引用上与其他同步块交互,在 thatRef.wait/notify/notifyAll()
和 上没有其他任何东西 。它本身的影响为零,你总是需要 2 个不同的线程同时触发一个同步块,在同一事物上同步,否则它什么也没做。
粘贴的代码片段已损坏:如果某些线程调用 decrement()
,其他线程可能会也可能不会观察到这一点,因为没有建立 CBCA 关系。任何读取 sharedVariable
的代码都需要锁定 ThreadDemo
,并且 decrement
方法需要获得一个 synchronized
属性。
请注意,拥有 incrementable/decrementable 的工作已经存在:AtomicInteger
,如果这是您的实际意图,您应该使用它(但我假设您只是将其写为一个例子)。
注意:java内存模型最好理解为邪恶的硬币。邪恶之处在于它会惹恼你:让代码在你所有的测试中每次都运行良好,第一周你把它放在生产服务器上,然后当那个重要的客户得到一个演示时,它休息。您必须编写代码,使 VM 永远不会抛硬币(或者更确切地说,抛硬币的结果不会影响您的代码),并且没有简单的方法可以知道正在抛硬币。线程很难正确,是的。为什么您认为现实世界中的大多数多线程代码都全部 通过消息总线或事务数据库进行线程间通信?任何时候任何代码接触任何地方的任何字段都会抛硬币,硬币的结果决定线程是使用该字段的本地克隆副本,还是从共享副本读取。因此,sharedVariable--
可能会导致只有您的线程可以看到或所有线程都可以看到的减量,具体取决于邪恶硬币的结果。如果你的代码做什么取决于翻转,你就搞砸了,你的测试不会捕捉到它。您可以通过在 sharedVariable--
和任何 cod 读取 sharedVariable
之间建立先行关系来避免抛硬币。 synchronized
是建立这种关系的几种方法之一。在网络上搜索 'java memory model' 'synchronized' 以获取更多信息 - 但请注意,这是非常复杂的内容。
假设在这个demo.Suppose中使用了2个线程increment()
代码块首先执行并获取当前的监视器object.does其他线程将无法执行该方法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);
}
}
不,不会。
-
方法上的
synchronized
只是语法糖,用于将方法的整个主体包装在synchronized (X) {}
中,其中 X 是this
实例方法和YourClass.class
对于静态的。这是一个严重的设计错误,除非你记录了 class 的锁定行为,如果你这样做 - 任何时候你锁定其他代码可以引用的东西(this
和YourClass.class
通常是public),您需要对此进行记录,并努力在未来的版本中支持您记录的内容。synchronized
在同一引用上与其他同步块交互,在thatRef.wait/notify/notifyAll()
和 上没有其他任何东西 。它本身的影响为零,你总是需要 2 个不同的线程同时触发一个同步块,在同一事物上同步,否则它什么也没做。
粘贴的代码片段已损坏:如果某些线程调用 decrement()
,其他线程可能会也可能不会观察到这一点,因为没有建立 CBCA 关系。任何读取 sharedVariable
的代码都需要锁定 ThreadDemo
,并且 decrement
方法需要获得一个 synchronized
属性。
请注意,拥有 incrementable/decrementable 的工作已经存在:AtomicInteger
,如果这是您的实际意图,您应该使用它(但我假设您只是将其写为一个例子)。
注意:java内存模型最好理解为邪恶的硬币。邪恶之处在于它会惹恼你:让代码在你所有的测试中每次都运行良好,第一周你把它放在生产服务器上,然后当那个重要的客户得到一个演示时,它休息。您必须编写代码,使 VM 永远不会抛硬币(或者更确切地说,抛硬币的结果不会影响您的代码),并且没有简单的方法可以知道正在抛硬币。线程很难正确,是的。为什么您认为现实世界中的大多数多线程代码都全部 通过消息总线或事务数据库进行线程间通信?任何时候任何代码接触任何地方的任何字段都会抛硬币,硬币的结果决定线程是使用该字段的本地克隆副本,还是从共享副本读取。因此,sharedVariable--
可能会导致只有您的线程可以看到或所有线程都可以看到的减量,具体取决于邪恶硬币的结果。如果你的代码做什么取决于翻转,你就搞砸了,你的测试不会捕捉到它。您可以通过在 sharedVariable--
和任何 cod 读取 sharedVariable
之间建立先行关系来避免抛硬币。 synchronized
是建立这种关系的几种方法之一。在网络上搜索 'java memory model' 'synchronized' 以获取更多信息 - 但请注意,这是非常复杂的内容。