公平锁:非常公平,不能够插队,必须先来后到!FIFO
非公平锁:非常不公平,可以插队(默认都是非公平)
递归锁
可重入锁synchronized 版本
package com.xing.lock; //Synchorized public class Demo01 { public static void main(String[] args) { Phone phone = new Phone(); new Thread(()->{ phone.sms(); },"a").start(); new Thread(()->{ phone.sms(); },"b").start(); } } class Phone{ public synchronized void sms(){ System.out.println(Thread.currentThread().getName() + "sms"); call(); //这里也有锁(sms锁 里面的call锁) } public synchronized void call(){ System.out.println(Thread.currentThread().getName() + "call"); } }
可重入锁Lock版本(递归锁 )
package com.xing.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Demo02 { public static void main(String[] args) { Phone2 phone = new Phone2(); new Thread(()->{ phone.sms(); },"a").start(); new Thread(()->{ phone.sms(); },"b").start(); } } class Phone2{ Lock lock = new ReentrantLock(); public void sms(){ lock.lock();//细节问题 第一把锁 //lock锁必须配对否则就会死在里面 try { System.out.println(Thread.currentThread().getName() + "sms"); call(); //这里也有锁(sms锁 里面的call锁) } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public synchronized void call(){ lock.lock();//第二把锁 try { System.out.println(Thread.currentThread().getName() + "call"); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } }
总结:两个锁只要获得外面的锁之后,就能够拿到里面的锁,Lock和synchronized 的区别在需要手动释放锁,并且枷锁的次数和释放的次数要一样
不断尝试直到成功为止
自己创建一个自己的锁用CAS完成
package com.xing.lock; import java.util.concurrent.atomic.AtomicReference; //自己写的自旋锁 public class SpinlockDemo { //Thread null AtomicReference<Thread> atomicReference = new AtomicReference(); // 加锁 public void myLock() { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "==>mylock"); //自旋锁 while (!atomicReference.compareAndSet(null, thread)) { } } // 解锁 public void myUnLock() { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "==>myunlock"); atomicReference.compareAndSet(thread, null); } }
然后去用线程调用
package com.xing.lock; import java.util.concurrent.TimeUnit; public class TestSpinlock { public static void main(String[] args) { /*ReentrantLock reentrantLock = new ReentrantLock(); reentrantLock.lock(); reentrantLock.unlock();*/ //底层的是自旋锁CAS实现的 SpinlockDemo spinlockDemo = new SpinlockDemo(); new Thread(()->{ spinlockDemo.myLock(); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }finally { spinlockDemo.myUnLock(); } },"T1").start(); //T1获得锁,T2一直在自旋。T1解锁后,T2才获得锁结束自旋 new Thread(()->{ spinlockDemo.myLock(); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }finally { spinlockDemo.myUnLock(); } },"T2").start(); } }
总结:B线程这里一定会等待T1完成比较交换这个时候自旋锁里已经被A占着了(自旋锁条件:线程不为空及自转)所以B线程会一直自旋到A线程完成CAS的比较交换完成才不在进行自旋操作再进入到myunlock里完成解锁操作
优点:
自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快。
缺点:
如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。
上面Java实现的自旋锁不是公平的,即无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。
死锁测试,怎么排除死锁
package com.xing.lock; import single.LazyMan; import java.util.concurrent.TimeUnit; public class DeadlockDemo { public static void main(String[] args) { String lockA = "lockA"; String lockB = "lockB"; // A想拿B new Thread(new MyThread(lockA,lockB),"T1").start(); // B想拿A new Thread(new MyThread(lockB,lockA),"T2").start(); } } class MyThread implements Runnable{ // 两个资源 private String lockA; private String lockB; public MyThread(String lockA, String lockB) { this.lockA = lockA; this.lockB = lockB; } @Override public void run() { synchronized (lockA){ //lockA想拿B System.out.println(Thread.currentThread().getName() + "lock"+ lockA + "=> get" + lockB); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB){ //B想拿A System.out.println(Thread.currentThread().getName() + "lock" + lockB + "=> get" + lockA); } } } }
1、使用jps -l定位进程号
例如:在终端输入jps -l
2、使用jstack 进程号 找到死锁问题
例如:jstack 11444