Java锁机制和同步

        今天看到ReentrantLock重入锁,想到了synchronized关键字同步锁,然后上网查它们的区别,就发现还有什么互斥锁、自旋锁、读写锁等,搞得我晕乎乎的,所以就花了一些时间总结了一下Java的锁机制。

常见的术语概念

        同步锁是基于线程安全来讲的,由synchronized关键字修饰提供,然后它更强调的是多线程运行时的同步关系,简单的说就是多线程的运行顺序不能乱。讲细一些是由于CPU的时间片轮换可能导致多个线程会交替执行,而同步是保证一个线程执行完在执行下一个线程。

        互斥锁更多的是对于临界资源的互斥,更加强调的是对于临界资源的访问必须是一个一个的来,而不能是一起访问。

        自旋锁的话,其实与互斥锁很类似,只不过一般的互斥锁都是以休眠线程来实现锁,而自旋锁则是以循环来实现锁。也因此起名为自旋。

        重入锁则是允许持有锁的线程重复加锁,而当锁已经被其他线程持有,当前线程会被阻塞。

1、synchronized同步锁

        代码块声明为synchronized,有两个重要后果,通常是指改代码具有原子性和可见性。

        1)、原子性意味着每个时刻,只有一个线程能够执行一段代码,这段代码通过一个monitor object保护。从而防止多个线程在更行共享状态时相互冲突。

        2)、可见性则更为微妙,它会忽略内存缓冲和编译器优化的各种反常行为。它必须确保释放锁之前对共享数据做出的更改对于随后获得该所的另一个线程是可见的。

        作用:如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。

     限制: 

        1)、它无法中断一个正在等候获得锁的线程;
        2)、也无法通过投票得到锁,如果不想等下去,也就没法得到锁;
        3)、同步还要球锁的释放只能在与获得锁所在的堆栈帧中进行,多数情况下,这没问题(而且与异常处理交互得很好),但是,确实存在一些非块结构的锁定更合适的情况。

2、互斥锁(synchronized&Lock)

        待总结

3、自旋锁

         自旋锁是采用让当前线程不停的在循环体内执行实现的,当循环条件被其他线程改变时才能进入临界区。如下:

public class SpinLock {

  private AtomicReference<Thread> sign =new AtomicReference<>();

  public void lock(){
    Thread current = Thread.currentThread();
    while(!sign .compareAndSet(null, current)){
    }
  }

  public void unlock (){
    Thread current = Thread.currentThread();
    sign .compareAndSet(current, null);
  }
}
        使用了CAS原子操作,lock函数将owner设置为当前线程,并且预测原来的值为空。unlock函数将owner设置为null,并且预测为当前线程。

        当有第二个线程调用lock操作时由于owner值不为空,导致循环一直被执行,直至第一个线程调用unlock函数将owner设置为null,并且预测值为当前线程。

        由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间短。适合使用自旋锁。

4、可重入锁(ReentrantLock)以及读写锁

        Lock接口有三个实现类,一个是ReentrantLock,另两个是ReentrantReadWriterLock类中的两个静态内部类ReadLock和WriterLock。

      ReentrantLock可重入锁

                当一个线程获取了该锁,计数器会变成1,其他线程在获取该锁时候发现锁的所有者不是自己所以被阻塞,但是当获取该锁的线程再次获取锁时候发现锁拥有者是自己会把计数器值+1,当释放锁后计数器会-1,当计数器为0时候,锁里面的线程标示重置为null,这时候阻塞的线程会获取被唤醒来获取该锁。
                ReentrantLock相比synchronized添加了类似锁投票、定时锁等候和中断锁等候的一些特性。
                缺陷:ReentrantLock必须在finally块中释放。否则,如果受保护的代码将抛出异常,锁就可能永远得不到释放!这种内存泄漏的问题当爆发出来时很难发现。而相对的使用synchronized同步就不存在这个问题,JVM将确保锁会获得自动释放。

总结:最后想强调的是要明确各种锁称呼的含义,比如可重入锁和同步锁也都属于互斥锁,只是我们以不同的特性去定义的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值