java 锁

1. 公平锁

指多个线程按照请求锁的顺序来获取锁,类似排队打饭,先来后到。

公平锁就是很公平,在并发环境中,每个线程在获取锁时会先查看此锁维护的等待队列,如果为空或者当前时等待队列的第一个,就占有锁,否则就加入等待队列中,以后会按照FIFO(先进先出)的规则从队列中去到自己

2.非公平锁

是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请锁的线程优先获取锁。在高并发的情况下,可能会造成优先级反转或者饥饿现象

非公平锁比较粗鲁,上来先尝试占有锁,如果尝试失败,就再采取类似于公平锁的那种方式。

非公平锁的优点在于吞吐量比公平锁大

对于Synchronized而言,也是一种非公平锁


并发包中的ReentrantLock可以指定构造函数的boolean 类型来得到公平锁或者非公平锁,默认非公平锁

  1. Lock a = new ReentrantLock(true);//公平锁
  2. Lock b = new ReentrantLock(false);//非公平锁
  3. Lock c = new ReentrantLock();//默认非公平锁

3. 可重入锁 (又名递归锁)

指的时同一个线程外层函数获取锁后,内层递归函数仍然能获取该锁的代码,在同一个线程的外层方法获取锁的时候,在进入内层方法或自动获取锁。也就是说 线程可以进入任何一个它已经拥有锁所同步的代码块。

如下代码,可重入锁的意思就是,当线程获取了method01的锁后,里面再访问加锁的method02时,无需再次获取method02的锁,默认有这个锁,同上来说,你有了进你家大门钥匙,那么你就可以去你家厕所。

  1. public syncronized void method01(){
  2. method02();
  3. }
  4. public syncronized void method02(){
  5. }

下面代码执行结果:

set 的进程信息:Thread-0
get 的进程信息:Thread-0
set 的进程信息:Thread-1
get 的进程信息:Thread-1

虽然set也加锁了,但是get得到锁后,相当于也得到了set的锁

  1. package com.bowen;
  2. import java.util.concurrent.locks.Lock;
  3. import java.util.concurrent.locks.ReentrantLock;
  4. class TestReentantLock implements Runnable{
  5. Lock lock = new ReentrantLock();
  6. @Override
  7. public void run() {
  8. get();
  9. }
  10. public void get(){
  11. lock.lock();
  12. try {
  13. set();
  14. System.out.println("get 的进程信息:"+Thread.currentThread().getName());
  15. }finally{
  16. lock.unlock();
  17. }
  18. }
  19. public void set(){
  20. lock.lock();
  21. try {
  22. System.out.println("set 的进程信息:"+Thread.currentThread().getName());
  23. }finally{
  24. lock.unlock();
  25. }
  26. }
  27. }
  28. public class TestLock {
  29. public static void main(String[] args) {
  30. TestReentantLock a = new TestReentantLock();
  31. Thread t1 = new Thread(a);
  32. Thread t2 = new Thread(a);
  33. t1.start();
  34. t2.start();
  35. }
  36. }

4. 自旋锁(spinlock)

是指尝试获取锁的线程不会立即阻塞,而是采取循环的方式去尝试获取锁。这样好处时减少线程上下文切换的消耗,缺点是会循环消耗CPU .

典型例子:unsafe.getAndAddInt方法

  1. public final int getAndAddInt(Object var1, long var2, int var4){
  2. int var5;
  3. do{
  4. var5 = this.getIntVolatile(var1, var2);
  5. }while(!this.compareAndSwapInt(var1, var2, var5+var4));
  6. return var5;
  7. }

手写自旋锁

  1. //自旋锁
  2. class TestSpinLock{
  3. AtomicReference<Thread> atomicReference = new AtomicReference<>();
  4. public void myLock(){
  5. Thread thread = Thread.currentThread();
  6. System.out.println(Thread.currentThread().getName() +"come in lock~!");
  7. while(!atomicReference.compareAndSet(null, thread)){
  8. }
  9. }
  10. public void myUnLock(){
  11. Thread thread = Thread.currentThread();
  12. System.out.println(Thread.currentThread().getName() +"come in unlock~!");
  13. atomicReference.compareAndSet(thread, null);
  14. }
  15. }
  16. public class TestLock {
  17. public static void main(String[] args) {
  18. // TestReentantLock a = new TestReentantLock();
  19. // Thread t1 = new Thread(a);
  20. // Thread t2 = new Thread(a);
  21. // t1.start();
  22. // t2.start();
  23. TestSpinLock testSpinLock = new TestSpinLock();
  24. new Thread(()->{
  25. testSpinLock.myLock();
  26. try {
  27. TimeUnit.SECONDS.sleep(5);
  28. }catch (InterruptedException e){}
  29. testSpinLock.myUnLock();
  30. },"AA").start();
  31. new Thread(()->{
  32. testSpinLock.myLock();
  33. try {
  34. TimeUnit.SECONDS.sleep(5);
  35. }catch (InterruptedException e){}
  36. testSpinLock.myUnLock();
  37. },"BB").start();
  38. }
  39. }
  40. //结果
  41. AAcome in lock~!
  42. BBcome in lock~!
  43. AAcome in unlock~!
  44. BBcome in unlock~!

5. 独占锁(写锁)

指该锁一次只能被一个线程持有。对ReentrantLock和ASynchronized 是独占锁

6. 共享锁(读锁)

指该锁可被多个线程所持有,对ReentrantReadWriteLock其读锁是共享锁,写锁是独占锁,

读的共享锁可以保证并发读是非常高效的,读写,写读,写写的过程互斥