互斥同步
java提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是jvm实现的synchronized,而另一个是jdk实现的ReentrantLock。
synchronized
1.同步一个代码块
public void func() {
synchronized (this) {
// ...
}
}
它只作用于同一个对象,如果调用两个对象以上的同步代码块,就不会进行同步。
对于以下代码,使用ExecutorSrevice执行了两个线程,由于调用的是同一个对象的同步代码块,那么线程会进行同步,当一个线程进入同步语句块的时候,另一个线程就不能进入。
import java.util.concurrent.*;
public class SynchronizedExemple{
private static class SychronizedExempleClass{
public void fun(){
synchronized(this){
for(int i=0;i<10;i++){
System.out.println(i);
}
}
}
}
public static void main(String[]args){
ExecutorService executorService=Executors.newCachedThreadPool();
SychronizedExempleClass e=new SychronizedExempleClass();
executorService.execute(()->e.fun());
executorService.execute(()->e.fun());
executorService.shutdown();
}
}
2.同步一个方法
同步一个方法时和同步一个代码块相同,作用于同一个对象
public synchronized void fun(){
//....
}
3.同步一个类
作用于整个类,所以说两个线程调用同一个类的不同对象上的这种同步语句,同样会进行同步。
public void fun(){
synchronized(SynchronizedExemple.class){
//....
}
}
下面代码调用两个线程访问两个不同对象的同步代码区。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SynchronizedClassExemple {
private static class SynchronClass{
public void fun(){
synchronized (SynchronClass.class){
for(int i=0;i<10;i++)
System.out.println(i);
}
}
}
public static void main(String[]args){
SynchronClass e1=new SynchronClass();
SynchronClass e2=new SynchronClass();
ExecutorService executorService= Executors.newCachedThreadPool();
executorService.execute(()->e1.fun());
executorService.execute(()->e2.fun());
executorService.shutdown();
}
}
4.同步一个静态方法
public synchronized void fun(){
//..
}
作用于整个类。
ReentrantLock
ReentrantLock是java.until.concurrent(J.U.C)包中的锁。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExemple {
private static class LockExemple{
Lock lock=new ReentrantLock();
public void fun(){
lock.lock(); //上锁
try{
for(int i=0;i<10;i++){
System.out.println(i);
}
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock(); //解锁
}
}
}
public static void main(String[]args){
LockExemple e=new LockExemple();
ExecutorService executorService= Executors.newCachedThreadPool();
executorService.execute(()->e.fun());
executorService.execute(()->e.fun());
executorService.shutdown();
}
}
两种锁的比较
1.锁的实现
synchronized是JVM实现的,而ReentrantLock是J.U.C实现的。
2.性能
新版本的java对synchronized进行了许多的优化,例如自旋锁,偏向锁,等。与ReentrantLock大致相同。
3.等待可中断
当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。ReentrantLock 可中断,而 synchronized 不行。
4.公平锁
公平锁值得是多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。synchronized锁是非公平的,而ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。
5.锁绑定多个条件
一个 ReentrantLock 可以同时绑定多个 Condition 对象。
使用选择
除非需要使用 ReentrantLock 的高级功能,否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放,但是使用ReentrantLock灵活度更高。