如果我只使用synchronized
,而不是wait
/notify
方法,它仍然是线程安全的吗?
有什么不同?
如果我只使用synchronized
,而不是wait
/notify
方法,它仍然是线程安全的吗?
有什么不同?
Usingsynchronized
使方法/块一次只能在线程上访问。所以,是的,它是线程安全的。
这两个概念是结合在一起的,而不是相互排斥的。使用时,wait()
您需要拥有该对象上的监视器。所以你需要synchronized(..)
在此之前拥有它。Using.wait()
使当前线程停止,直到另一个线程调用.notify()
它等待的对象。这是对 的补充synchronized
,它只是确保只有一个线程将进入块/方法。
因此,在对一个关于这个问题的面试问题感到尴尬之后,我决定再次查找并理解它第十亿次。
synchronized
块使代码线程安全。毫无疑问。When wait()
and notify()
or notifyAll()
come in 是您尝试编写更高效代码的地方。例如,如果您有一个多个线程共享的项目列表,那么如果您将它放在synchronized
监视器的块中,那么线程线程将不断跳入并在上下文切换期间来回运行代码..... .即使是空列表!
因此,wait() 用于监视器(synchronized(..) 中的对象)作为一种机制来告诉所有线程冷静下来并停止使用 cpu 周期,直到另行通知或 notifyAll()。
所以像:
synchronized(monitor) {
if( list.isEmpty() )
monitor.wait();
}
...别的地方...
synchronized(monitor){
list.add(stuff);
monitor.notifyAll();
}
将方法设为 同步有两个效果:
首先,同一对象上的同步方法的两次调用不可能交错。当一个线程正在为一个对象执行同步方法时,所有其他为同一对象调用同步方法的线程都会阻塞(暂停执行),直到第一个线程处理完该对象
其次,当同步方法退出时,它会自动与任何后续对同一对象的同步方法调用建立起之前的关系。这保证了对象状态的更改对所有线程都是可见的。
同步帮助您保护关键代码。
如果要在多个线程之间建立通信,则必须使用wait()和notify() / notifyAll()
wait()
: 使当前线程等待,直到另一个线程为此对象调用 notify() 方法或 notifyAll() 方法。
notify()
: 唤醒正在此对象的监视器上等待的单个线程。如果有任何线程正在等待该对象,则选择其中一个被唤醒。
notifyAll()
:唤醒所有在这个对象的监视器上等待的线程。线程通过调用其中一个等待方法在对象的监视器上等待。
使用 wait() 和 notify() 的简单用例:生产者和消费者问题。
消费者线程必须等到生产者线程产生数据。wait() 和 notify() 在上述情况下很有用。在一段时间内,已经引入了更好的替代方案。请参阅此高级并发教程页面。
简单来说:
用于synchronized
保护数据的关键部分并保护您的代码。
如果您想以安全的方式在多个线程之间建立通信,请使用wait()
和notify()
与同步一起使用,这些线程相互依赖。
相关的 SE 问题:
有效的 Java 项目 69:“鉴于正确使用等待和通知的困难,您应该改用更高级别的并发实用程序。”
尽可能避免使用 wait() 和 notify(): use或java.util.concurrent 中synchronized
的其他实用程序。
如果“同一对象”的 2 个线程试图获取锁,则使用同步块。由于对象类持有锁,它知道给谁。然而,如果 2 个对象(obj1 的 t1 和 t2 以及 obj 2 的 t3 和 t4)的 2 个线程(比如 t2 和 t4)尝试获取锁,则 obj1 将不知道 obj2 的锁,而 obj2 将不知道 obj1 的锁。因此使用了等待和通知方法。
例如:
//example of java synchronized method
class Table{
synchronized void printTable(int n){//synchronized method
for(int i=1;i<=5;i++){
System.out.println(n*i);
try{
Thread.sleep(400);
}catch(Exception e){System.out.println(e);}
}
}
}
class MyThread1 extends Thread{
Table t;
MyThread1(Table t){
this.t=t;
}
public void run(){
t.printTable(5);
}
}
class MyThread2 extends Thread{
Table t;
MyThread2(Table t){
this.t=t;
}
public void run(){
t.printTable(100);
}
}
public class TestSynchronization2{
public static void main(String args[]){
Table obj = new Table();//only one object
MyThread1 t1=new MyThread1(obj);
MyThread2 t2=new MyThread2(obj);
t1.start();
t2.start();
}
}
两个线程 t1 和 t2 属于同一个对象,因此同步在这里可以正常工作。然而,
class Table{
synchronized void printTable(int n){//synchronized method
for(int i=1;i<=5;i++){
System.out.println(n*i);
try{
Thread.sleep(400);
}catch(Exception e){System.out.println(e);}
}
}
}
class MyThread1 extends Thread{
Table t;
MyThread1(Table t){
this.t=t;
}
public void run(){
t.printTable(5);
}
}
class MyThread2 extends Thread{
Table t;
MyThread2(Table t){
this.t=t;
}
public void run(){
t.printTable(100);
}
}
public class TestSynchronization2{
public static void main(String args[]){
Table obj = new Table();
Table obj1 = new Table();
MyThread1 t1=new MyThread1(obj);
MyThread2 t2=new MyThread2(obj1);
t1.start();
t2.start();
}
}
当你运行上述程序时,由于每个线程属于不同的对象,同步不起作用,因此你应该在这里使用等待和通知。
当您想在同步块内等待某些条件(例如用户输入)时,需要等待/通知。
典型用法:
synchronized(obj) {
// do something
while(some condition is not met) {
obj.wait();
}
// do something other
}
假设您不使用 wait()。然后,您必须实施繁忙的循环轮询您想要的条件,这对性能不利。
synchronized(obj) {
// do something
while(some condition is not met) { // busy loop }
// do something other
}
重要提示:即使一个线程被其他线程的 notify() 或 notifyAll() 唤醒,唤醒线程也不能保证立即恢复执行。如果有其他线程等待对同一对象执行同步块,则唤醒线程应该与线程竞争。