互斥同步

  java提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是jvm实现的synchronized,而另一个是jdk实现的ReentrantLock

synchronized

1.同步一个代码块

public void func() {
    synchronized (this) {
        // ...
    }
}

  它只作用于同一个对象,如果调用两个对象以上的同步代码块,就不会进行同步。

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

  对于以下代码,使用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灵活度更高

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄