Synchronized normal method 相当于 Synchronized statement (使用此)
class A {
public synchronized void methodA() {
// all function code
}
equivalent to
public void methodA() {
synchronized(this) {
// all function code
}
}
}
class A {
public static synchronized void methodA() {
// all function code
}
equivalent to
public void methodA() {
synchronized(A.class) {
// all function code
}
}
}
同步语句(使用变量)
class A {
private Object lock1 = new Object();
public void methodA() {
synchronized(lock1 ) {
// all function code
}
}
}
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();
}
}
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
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
15 回答
synchronized
关键字可防止多个线程对代码块或对象的并发访问 . 默认情况下,Hashtable
是synchronized
,因此一次只能有一个线程访问该表 .在使用
non-synchronized
构造(如HashMap
)时,必须在代码中构建线程安全功能以防止内存一致性错误 .据我所知,synchronized基本上意味着编译器在你的方法周围写一个monitor.enter和monitor.exit . 因此它可能是线程安全的,具体取决于它的使用方式(我的意思是你可以使用同步方法编写一个不是线程安全的对象,具体取决于你的类所做的事情) .
好吧,我认为我们有足够的理论解释,所以请考虑这段代码
注意:
synchronized
阻塞下一个线程's call to method test() as long as the previous thread'的执行未完成 . 线程可以一次访问一个方法 . 如果没有synchronized
,所有线程都可以同时访问此方法 .当一个线程调用对象的synchronized方法'test'时(这里的对象是'TheDemo'类的实例)它获取该对象的锁,任何新线程都不能调用相同对象的ANY synchronized方法,只要前一个线程获得锁的人不会释放锁 .
当调用类的任何静态同步方法时,会发生类似的事情 . 线程获取与类关联的锁(在这种情况下,任何线程都可以调用该类实例的任何非静态同步方法,因为该对象级锁仍然可用) . 只要当前持有锁的线程没有释放类级别锁,任何其他线程将无法调用该类的任何静态同步方法 .
Output with synchronised
Output without synchronized
synchronized
关键字导致线程在进入方法时获得锁定,因此只有一个线程可以同时执行该方法(对于给定的对象实例,除非它是静态方法) .这通常被称为使类线程安全,但我会说这是一个委婉说法 . 虽然同步保护Vector的内部状态不会被破坏,但这通常不会帮助Vector的用户 .
考虑一下:
即使所涉及的方法是同步的,因为它们被单独锁定和解锁,两个不幸的定时线程可以创建具有两个元素的向量 .
因此,实际上,您还必须在应用程序代码中进行同步 .
因为当你不需要它时方法级同步是昂贵的而且b)当你需要同步时不够用,所以现在有未同步的替换(在Vector的情况下为ArrayList) .
最近,并发软件包已经发布,有许多聪明的实用程序可以解决多线程问题 .
synchronized是Java中的一个关键字,用于在多线程环境中的关系之前发生,以避免内存不一致和线程干扰错误 .
synchronized simple意味着没有两个线程可以同时访问块/方法 . 当我们说类的任何块/方法被同步时,它意味着一次只有一个线程可以访问它们 . 在内部,尝试访问它的线程首先锁定该对象,并且只要该锁不可用,其他线程就不能访问该类实例的任何同步方法/块 .
注意,另一个线程可以访问未定义为同步的同一对象的方法 . 线程可以通过调用释放锁
synchronized
表示在多个中在线程环境中,具有synchronized
方法/块的对象不允许两个线程同时访问synchronized
方法/块代码 . 这意味着一个线程无法读取而另一个线程更新它 .第二个线程将等待第一个线程完成其执行 . 开销是速度,但优点是保证数据的一致性 .
如果您的应用程序是单线程的,
synchronized
块不提供好处 .Synchronized normal method
相当于Synchronized statement
(使用此)Synchronized static method
相当于Synchronized statement
(使用类)同步语句(使用变量)
对于
synchronized
,我们同时拥有Synchronized Methods
和Synchronized Statements
. 但是,Synchronized Methods
与Synchronized Statements
类似,所以我们只需要了解Synchronized Statements
.=>基本上,我们会有
这是2认为有助于理解
synchronized
每个对象/类都有一个
intrinsic lock
与之关联 .当线程调用
synchronized statement
时,它会自动获取该synchronized statement's
对象的intrinsic lock
,并在方法返回时释放它 . 只要一个线程拥有intrinsic lock
, NO other 线程就可以获得 SAME lock =>线程安全 .=>当
thread A
调用synchronized(this){// code 1}
=>所有块代码(内部类)中有synchronized(this)
且所有synchronized normal method
(内部类)被锁定因为 SAME 锁定 . 它将在thread A
unlock("// code 1"完成)后执行 .此行为类似于
synchronized(a variable){// code 1}
或synchronized(class)
.SAME LOCK => lock(不依赖于哪种方法?或哪些语句?)
使用synchronized方法或synchronized语句?
我更喜欢
synchronized statements
,因为它更具扩展性 . 例如,将来,您只需要同步一部分方法 . 例如,你有2个同步方法并且 don't have any 彼此相关,但是当一个线程运行一个方法时,它会阻塞另一个方法(它可以通过使用synchronized(a variable)
来阻止) .但是,应用synchronized方法很简单,代码看起来很简单 . 对于某些类,只有1个同步方法,或者类中所有同步方法彼此相关=>我们可以使用
synchronized method
使代码更短更容易理解注意
(它不是与
synchronized
相关,它是对象和类之间或非静态和静态的不同) .当您使用
synchronized
或普通方法或synchronized(this)
或synchronized(non-static variable)
时,它将基于每个对象实例进行同步 .当您使用
synchronized
或静态方法或synchronized(class)
或synchronized(static variable)
时,它将基于类同步参考
https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
希望它有所帮助
同步只是意味着如果与特定对象使用同步块,则与单个对象关联的多个线程可以防止脏读和写 . 为了让您更清晰,我们举个例子:
我们创建了两个MyRunnable类对象,runnable1与线程1共享,而线程3和runnable2仅与线程2共享 . 现在当t1和t3开始时没有使用synchronized时,PFB输出表明线程1和3同时影响var值,对于线程2,var有自己的内存 .
使用Synchronzied,线程3等待线程1在所有场景中完成 . 获取了两个锁,一个在runnable1上,由线程1和线程3共享,另一个在runnable2上,仅由线程2共享 .
synchronized
关键字是关于读取和写入相同变量,对象和资源的不同线程 . 这不是Java中的一个简单主题,但这里引用了Sun:简而言之:如果有两个线程正在读取和写入相同的'resource',比如名为
foo
的变量,则需要确保这些线程以原子方式访问变量 . 如果没有synchronized
关键字,您的线程1可能看不到更改线程2对foo
,或者更糟糕的是,它可能只有一半更改 . 这不是你逻辑上所期望的 .同样,这是Java中一个非常重要的主题 . 要了解更多信息,请在此处探索有关SO和Interwebs的主题:
Concurrency
Java Memory Model
继续探索这些主题,直到名称"Brian Goetz"与您大脑中的术语"concurrency"永久关联 .
概述
Java中的同步关键字与线程安全有关,也就是说,当多个线程读取或写入相同的变量时 .
这可以直接发生(通过访问相同的变量)或间接发生(通过使用使用另一个访问同一变量的类的类) .
synchronized关键字用于定义一个代码块,其中多个线程可以安全的方式访问同一个变量 .
更深
语法方面
synchronized
关键字将Object
作为其参数(称为锁定对象),然后是{ block of code }
.当执行遇到此关键字时,当前线程会尝试"lock/acquire/own"(自己选择)锁定对象,并在获取锁定后执行相关的代码块 .
保证对同步代码块内的变量的任何写入对于使用相同锁定对象在同步代码块内类似地执行代码的每个其他线程是可见的 .
一次只有一个线程可以保持锁定,在此期间,尝试获取相同锁定对象的所有其他线程将等待(暂停执行) . 执行退出同步代码块时将释放锁定 .
同步方法:
将
synchronized
关键字添加到方法定义等于将整个方法体包装在同步代码块中,其中锁定对象为this
(例如方法)和ClassInQuestion.getClass()
(对于类方法) .static
关键字的方法 .static
关键字的方法 .技术
如果没有同步,则无法保证读取和写入的顺序发生,可能会使变量带有垃圾 .
(例如,一个变量最终可能会被一个线程写入的一半位和另一个线程写入的一半位,最终使变量处于两个线程都没有尝试写入的状态,而是两者的组合混乱 . )
在另一个线程读取它之前(挂钟时间)完成一个线程中的写操作是不够的,因为硬件可能已经缓存了变量的值,并且读取线程将看到缓存的值而不是写入的值它 .
结论
因此,在Java的情况下,您必须遵循Java内存模型以确保不会发生线程错误 .
换句话说:使用同步,原子操作或在引擎盖下使用它们的类 .
线程主要进行通信通过共享对字段和对象引用字段的访问来引用 . 这种通信形式非常有效,但可能出现两种错误: thread interference and memory consistency errors . 防止这些错误所需的工具是同步 .
同步块或方法可防止线程干扰并确保数据一致 . 在任何时间点,只有一个线程可以通过获取锁来访问同步块或方法(临界区) . 其他线程将等待释放锁以访问关键部分 .
将
synchronized
添加到方法定义或声明时,方法会同步 . 您还可以使用方法同步特定的代码块 .这意味着只有一个线程可以通过获取锁来访问关键部分 . 除非此线程释放此锁,否则所有其他线程将不得不等待获取锁 . 他们无权进入关键部分而无法获得锁定 .
这可以由程序员负责识别应用程序中的关键部分并相应地保护它 . Java提供了一个框架来保护您的应用程序,但是保护所有部分的位置和内容是程序员的责任 .
来自java文档的更多细节page
Intrinsic Locks and Synchronization:
每个对象都有一个与之关联的内在锁 . 按照惯例,在访问对象之前需要对对象's fields has to acquire the object'的内部锁进行独占和一致访问的线程,然后在完成它们时释放内部锁 .
一个线程被称为在获得锁定和释放锁定之间拥有内在锁定 . 只要一个线程拥有一个内部锁,没有其他线程可以获得相同的锁 . 另一个线程在尝试获取锁时将阻塞 .
使方法同步有两个effects:
当一个线程正在为对象执行同步方法时,所有其他线程都会调用同一对象的同步方法(暂停执行),直到第一个线程完成对象为止 .
这可以保证对所有线程都可以看到对象状态的更改 .
寻找其他同步替代方案:
Avoid synchronized(this) in Java?
以下是The Java Tutorials的解释 .
请考虑以下代码:
public synchronized void increment(){
C ;
}
public synchronized void decrement(){
C - ;
}
public synchronized int value(){
返回c;
}
}
如果count是SynchronizedCounter的一个实例,那么使这些方法同步有两个影响:首先,同一个对象上的两个同步方法的调用不可能交错 . 当一个线程正在为对象执行同步方法时,所有其他线程都会调用同一对象的同步方法(暂停执行),直到第一个线程完成对象为止 . 其次,当同步方法退出时,它会自动与同一对象的同步方法的任何后续调用 Build 先发生关系 . 这可以保证对所有线程都可以看到对象状态的更改 .
把它想象成你可能在足球场上发现的一种旋转门 . 人们想要进入平行的蒸汽,但在旋转门处它们是“同步的” . 一次只能有一个人通过 . 所有想要通过的人都会这样做,但他们可能要等到他们能够完成 .
缺少的其他答案是一个重要方面:记忆障碍 . 线程同步基本上由两部分组成:序列化和可见性 . 我建议所有人google for "jvm memory barrier",因为这是一个非常重要且非常重要的主题(如果您修改多个线程访问的共享数据) . 完成后,我建议查看java.util.concurrent包的类,这些类有助于避免使用显式同步,这反过来有助于保持程序简单有效,甚至可以防止死锁 .
一个这样的例子是ConcurrentLinkedDeque . 与command pattern一起,它允许通过将命令填充到并发队列中来创建高效的工作线程 - 不需要显式同步,不需要死锁,不需要显式sleep(),只需通过调用take()轮询队列 .
简而言之:"memory synchronization"隐式发生在你启动一个线程,一个线程结束,你读一个volatile变量,你解锁一个监视器(保留一个同步的块/函数)等 . 这个"synchronization"影响(在某种意义上"flushes")之前完成的所有写操作特别的行动 . 在上述ConcurrentLinkedDeque的情况下,文档"says":
这种隐式行为是一个有点有害的方面,因为大多数没有太多经验的Java程序员会因为它而花费很多时间 . 然后,在Java没有按照“假设”在 生产环境 中执行不同工作负载的情况时突然发现这个线程 - 并且很难测试并发问题 .