公平锁是指当锁可用时,在锁上等待时间最长的线程将获得锁的使用权,必须先来后到。
//ReentrantLock(true)设置为公平锁 public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
非公平锁则随机分配这种使用权,可以插队。有时候,一些线程的执行时间很长,如果其他线程需要锁的话就需要等待很长的时间,所以为了提供效率,我们一般使用非公平锁
//ReentrantLock()默认是非公平锁 public ReentrantLock() { sync = new NonfairSync(); }
可重入锁又叫递归锁,指同一个线程在外层方法获取锁的时候,进入内层方法会自动获取锁。JDK 中基本都是可重入锁,避免死锁的发生。
synchronized 可重入锁
public class Demo01 { public static void main(String[] args) { Phone phone = new Phone(); new Thread(()->{ phone.sms(); },"A").start(); } class Phone{ public synchronized void sms(){ System.out.println(Thread.currentThread().getName()+"=> sms"); call();//内层方法 } //内层方法的锁 public synchronized void call(){ System.out.println(Thread.currentThread().getName()+"=> call"); } }
Lock 可重入锁
public class Demo02 { public static void main(String[] args) { Phone2 phone = new Phone2(); new Thread(()->{ phone.sms(); },"A").start(); } class Phone2{ Lock lock=new ReentrantLock(); public void sms(){ lock.lock(); //细节:这个是两把锁,分别是sms和call //lock锁必须配对,加了几次就要释放几次,否则就会死锁在里面 try { System.out.println(Thread.currentThread().getName()+"=> sms"); call();//这里也有一把锁 } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void call(){ lock.lock(); try { System.out.println(Thread.currentThread().getName() + "=> call"); }catch (Exception e){ e.printStackTrace(); } finally { lock.unlock(); } } }
自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。尝试获取锁的线程不会立即阻塞,采用循环的方式尝试获取锁!减少上下文的切换!缺点会消耗CPU
自定义自旋锁
public class SpinlockDemo { 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); } }
测试自旋锁
public class TestSpinLock { public static void main(String[] args) throws InterruptedException { SpinLockDemo lockDemo = new SpinLockDemo(); new Thread(()->{ lockDemo.myLock(); try{ TimeUnit.SECONDS.sleep(5); }catch (Exception e){ e.printStackTrace(); }finally { lockDemo.myUnlock(); } },"T1").start(); TimeUnit.SECONDS.sleep(1); new Thread(()->{ lockDemo.myLock(); try{ TimeUnit.SECONDS.sleep(1); }catch (Exception e){ e.printStackTrace(); }finally { lockDemo.myUnlock(); } },"T2").start(); } }
线程T1先拿到锁,T2在自旋等待,直到T1释放了锁,T2才结束自旋,获取到锁。
死锁问题:多个线程互相拿着对方需要的资源,然后形成僵持。出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。
产生死锁的四个必要条件
死锁案例
import java.util.concurrent.TimeUnit; public class DeadLockDemo { public static void main(String[] args) { String lockA = "lockA"; String lockB = "lockB"; new Thread(new MyThread(lockA,lockB),"T1").start(); 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){ System.out.println(Thread.currentThread().getName()+ " lock:"+lockA + " => get" + lockB); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB){ System.out.println(Thread.currentThread().getName()+ " lock:"+lockB + " => get" + lockA); } } } }
运行结果:
线程T1已经占有了A锁,在等待B锁,线程T2已经占有了B锁,在等待A锁,两个线程互相僵持,导致程序运行阻塞。
如何排查死锁
使用 进行排查,
jps(Java Virtual Machine Process Status Tool)是 jdk 提供的一个查看当前java进程的小工具。
以上面死锁为例,进行排查:
1、使用 jps -l
定位进程号
2、使用jstack + 进程号
查看堆栈信息,用以分析线程情况