关于synchronized、volatile、ReentrantLock的区别与对比
目录
- 并发编程特性
- volatile
- 特性:
- 作用的地方:
- synchronized
- 特性
- 作用的地方
- 修饰方法,分为实例方法和静态方法
- 修饰代码块
- ReentrantLock
- 特性
- 作用的地方
- 做为成员变量使用格式:
- 做为局部变量使用格式:
- 对比
聊之前先说一下并发编程的3个特性。
并发编程特性
- 原子性:对共享资源的一组操作,要么成功要么失败,不会出现部分成功部分失败的情况。
- 可见性: 当线程获取到琐时,会拷贝一份共享资源到本地内存,释放锁时会将共享资源刷新到主内存中。可见性是指当共享资源发生变化时,其他线程都能够看到这个变化。
- 有序性:为了提高效率,编译器和处理器会对代码进行指令重排,单线程的情况下,指令重拍不会受到影响,多线程情况下可能会影响代码执行的正确性。有序性是指代码编写顺序和执行顺序是一致的。
volatile
volatile 是JVM提供的最轻量级的同步机制,编译器不会对其进行优化。
特性:
- volatile 只保证共享资源的可见性和有序性。
- 使用volatile修饰共享资源时,如果共享资源变化时,会直接将缓存中的数据写回到主内存中去,数据也是从主内存中读取,从而保证了可见性。
- volatile 底层是通过操作系统的内存屏障来实现的,由于使⽤了内存屏障,所以会禁⽌指令重排,从而就保证了有序性。
作用的地方:
volatile 只能用来修饰成员变量。
public class VolatileTest { private volatile static String staticVolatile; private volatile String memberVolatile; }
synchronized
synchronized关键字是java提供的内置锁来保证我们对共享资源的同步,它会自动加锁和释放锁,它的锁是非公平锁, synchronized关键字标记的地方会被编译器进行优化。synchronized会使线程串行执行,可能会造成线程阻塞。
特性
- synchronized关键字使线程串行化执行,所以保证了并发安全的3个特性,并且还拥有以下两个特性:
- 互斥性:同时只有一个线程能够访问synchronized方法或者同步代码块。
- 可重入性:synchronized是可重入锁,通俗解释可重入锁就是当一个线程获取到了某个对象锁或者类锁之后,这个线程在未释放锁之前,再调用该锁的其他synchronized方法或代码块时,不用再次重新获得锁。
作用的地方
synchronized关键字可用来修饰方法或者代码块。
修饰方法,分为实例方法和静态方法
- 修饰实例方法,对象锁
public synchronized void objectMethods(){ ..... }
- 修饰静态方法,类锁
public static synchronized void staticMethods(){ ..... }
修饰代码块
obj为对象的引用 对象锁
public void objectMethods(){ synchronized (obj){ } }
Object 为某个类 类锁
public void classLock(){ synchronized (Object.class){ } }
ReentrantLock
Lock 是 Java 5提供的一个具有锁机制的接口,ReentrantLock 是Lock的一个实现,内部是通过AQS(AbstractQueuedSynchronizer)实现的。ReentrantLock翻译过来是可重入锁,它和synchronized类似,ReentrantLock 需要手动加锁和释放锁, 相对于synchronized它更加灵活,提供了更多的方法。 ReentrantLock 有公平锁和非公平锁两种方式,默认是使用公平锁。
特性
ReentrantLock 是可重入的同步锁,所以它除了具有并发编程的三大特性,还具有可重入性。
作用的地方
ReentrantLock 是一个类,它既可以作为成员变量,也可以作为局部变量使用。做为成员变量和局部变量时,使用的方式有一点点不同,不管使用哪种方式,最后都别忘了要调用unlock()
方法手动释放锁。
做为成员变量使用格式:
private Lock globalLock = new ReentrantLock(); public void globalLock(){ if (globalLock.tryLock()) { try { } catch (Exception e) { }finally { globalLock.unlock(); } } }
上面的tryLock()
方法是尝试获取锁,如果获取成功返回true,否则返回false,也可以换成加了等待时间的 boolean tryLock(long time, TimeUnit unit)
方法,在设定的等待时间内获取锁成功则返回true,否则false。
做为局部变量使用格式:
public void lock(){ Lock lock = new ReentrantLock(); lock.lock(); try { }finally { lock.unlock(); } }
对比
简单对比一下三者之间的区别:
volatile | synchronized | ReentrantLock | |
是否是关键字 | 是 | 是 | 否 |
是否需要手动加锁/释放锁 | 否 | 否 | 是 |
是否能保证并发安全 | 否 | 是 | 是 |
是否是公平锁 | \ | 否 | 有公平锁和非公平锁两种实现 |
是否会阻塞线程 | 否 | 是 | 是 |
JVM是否会对其优化 | 否 | 是 | 否 |
特性 | 可见性、有序性 | 可见性、有序性、原子性 | 可见性、有序性、原子性 |
使用的地方 | 成员变量 | 方法、代码块 | 成员变量、局部变量 |
到此这篇关于关于synchronized、volatile、ReentrantLock的区别与对比的文章就介绍到这了,更多相关synchronized、volatile、ReentrantLock的区别内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!