我有一些关于同步关键字的用法和意义的问题。
synchronized关键字有什么意义? 什么时候应该同步方法? 它在程序上和逻辑上意味着什么?
我有一些关于同步关键字的用法和意义的问题。
synchronized关键字有什么意义? 什么时候应该同步方法? 它在程序上和逻辑上意味着什么?
当前回答
What the other answers are missing is one important aspect: memory barriers. Thread synchronization basically consists of two parts: serialization and visibility. I advise everyone to google for "jvm memory barrier", as it is a non-trivial and extremely important topic (if you modify shared data accessed by multiple threads). Having done that, I advise looking at java.util.concurrent package's classes that help to avoid using explicit synchronization, which in turn helps keeping programs simple and efficient, maybe even preventing deadlocks.
ConcurrentLinkedDeque就是这样一个例子。与命令模式一起,它允许通过将命令填充到并发队列中来创建高效的工作线程——不需要显式同步,不可能发生死锁,不需要显式sleep(),只需通过调用take()轮询队列。
简而言之:“内存同步”隐式地发生在线程启动、线程结束、读取volatile变量、解锁监视器(留下同步块/函数)等时候。这种“同步”影响(在某种意义上“刷新”)在该特定操作之前完成的所有写操作。在前面提到的ConcurrentLinkedDeque的情况下,文档“说”:
内存一致性影响:与其他并发集合一样, 对象放入线程之前的操作 ConcurrentLinkedDeque访问之后的happens -before操作 或者从另一个ConcurrentLinkedDeque中删除该元素 线程。
这种隐式行为在某种程度上是有害的,因为大多数没有太多经验的Java程序员会因此而得过其实。然后突然被这个线程绊倒,因为Java在生产中没有做它“应该”做的事情,因为有不同的工作负载——并且很难测试并发性问题。
其他回答
Java同步
volatile[About] => synchronized
synchronized块在Java中是多线程中的监视器。同一对象/类的同步块只能由单个线程执行,其他的都在等待。它可以帮助解决竞赛状况。
Java 5通过支持happens-before扩展了同步[关于]
监视器的解锁(同步块或方法退出)发生在同一监视器的每个后续锁定(同步块或方法进入)之前。
下一步是java.util.concurrent
synchronized关键字是什么?
线程主要通过共享对字段和引用字段所引用的对象的访问进行通信。这种通信形式非常高效,但可能会产生两种错误:线程干扰和内存一致性错误。防止这些错误所需的工具是同步。
同步块或方法可以防止线程干扰,并确保数据是一致的。在任何时候,只有一个线程可以通过获得锁来访问同步块或方法(临界区)。其他线程将等待锁释放来访问临界区。
方法何时同步?
将synchronized添加到方法定义或声明时,方法是同步的。您还可以在方法中同步特定的代码块。
它在语法上和逻辑上意味着什么?
这意味着只有一个线程可以通过获得锁来访问临界区。除非这个线程释放这个锁,否则所有其他线程将不得不等待获得一个锁。他们不能在没有锁的情况下进入临界区。
这不是魔法能办到的。程序员的职责是识别应用程序中的关键部分并相应地保护它。Java提供了一个框架来保护你的应用程序,但是在哪里以及保护哪些部分是程序员的责任。
更多细节,请参阅java文档页
内在锁和同步:
同步是围绕一个称为内在锁或监视器锁的内部实体构建的。内在锁在同步的两个方面都发挥作用:强制对对象状态的独占访问,并建立对可见性至关重要的happens-before关系。
每个对象都有一个与之相关的内在锁。按照惯例,需要独占和一致访问对象字段的线程必须在访问对象字段之前获得对象的内在锁,然后在使用完对象字段时释放内在锁。
线程在获得锁和释放锁之间拥有内在锁。只要一个线程拥有一个内在锁,其他线程就不能获得相同的锁。另一个线程在试图获取锁时将阻塞。
当线程释放一个内在锁时,在该操作和后续获得的任何相同锁之间建立happens-before关系。
使方法同步有两个效果:
首先,对同一对象的同步方法的两次调用不可能交织。
当一个线程正在为一个对象执行同步方法时,所有为同一对象调用同步方法的其他线程将暂停执行,直到第一个线程处理完该对象。
其次,当同步方法退出时,它自动与同一对象的同步方法的任何后续调用建立happens-before关系。
这保证了对对象状态的更改对于所有线程都是可见的。
寻找同步的其他替代方案:
避免在Java中同步(这)?
Synchronized的意思是,如果在特定对象上使用Synchronized块,那么与单个对象关联的多个线程可以防止脏读写。为了让你更清楚,让我们举个例子:
class MyRunnable implements Runnable {
int var = 10;
@Override
public void run() {
call();
}
public void call() {
synchronized (this) {
for (int i = 0; i < 4; i++) {
var++;
System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var);
}
}
}
}
public class MutlipleThreadsRunnable {
public static void main(String[] args) {
MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();
Thread t1 = new Thread(runnable1);
t1.setName("Thread -1");
Thread t2 = new Thread(runnable2);
t2.setName("Thread -2");
Thread t3 = new Thread(runnable1);
t3.setName("Thread -3");
t1.start();
t2.start();
t3.start();
}
}
我们已经创建了两个MyRunnable类对象,runnable1与线程1和线程3共享,而runnable2仅与线程2共享。 现在,当t1和t3启动时没有使用synchronized, PFB输出表明线程1和线程3同时影响var值,而对于线程2,var有自己的内存。
Without Synchronized keyword
Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -2 var value 12
Current Thread Thread -2 var value 13
Current Thread Thread -2 var value 14
Current Thread Thread -1 var value 12
Current Thread Thread -3 var value 13
Current Thread Thread -3 var value 15
Current Thread Thread -1 var value 14
Current Thread Thread -1 var value 17
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 18
使用Synchronzied,线程3在所有场景中等待线程1完成。有两个锁,一个在runnable1上,由线程1和线程3共享,另一个在runnable2上,仅由线程2共享。
Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -1 var value 12
Current Thread Thread -2 var value 12
Current Thread Thread -1 var value 13
Current Thread Thread -2 var value 13
Current Thread Thread -1 var value 14
Current Thread Thread -2 var value 14
Current Thread Thread -3 var value 15
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 17
Current Thread Thread -3 var value 18
synchronized关键字可以防止多个线程并发访问一个代码块或对象。Hashtable的所有方法都是同步的,因此一次只有一个线程可以执行其中的任何方法。
当使用非同步结构(如HashMap)时,必须在代码中构建线程安全特性以防止一致性错误。
synchronized关键字是关于不同的线程读写相同的变量、对象和资源。这在Java中不是一个微不足道的话题,但这里引用Sun的一段话:
同步方法可以实现简单的 防止线程的策略 干扰和记忆一致性 错误:如果对象是可见的 多于一个线程,全部读取或 写入该对象的变量为 通过同步方法完成。
简而言之:当你有两个线程读写同一个“资源”时,比如说一个名为foo的变量,你需要确保这些线程以原子的方式访问变量。如果没有synchronized关键字,线程1可能看不到线程2对foo所做的更改,或者更糟的是,它可能只改变了一半。这不是你逻辑上所期望的。
同样,这在Java中是一个重要的主题。要了解更多,请在这里探索关于SO和互联网的主题:
并发性 Java内存模型
继续探索这些话题,直到“Brian Goetz”这个名字在你的脑海中与“并发性”这个术语永久地联系在一起。