java中的锁
公平锁/非公平锁
可重入锁
指的是以线程为单位,当一个线程获取对象锁之后,这个线程可以再次获取本对象上的锁,而其他的线程是不可以的。
public class ReenterLockTest { public static void main(String[] args) { new Thread(()->{ reenterLock_DaMen(); }).start(); new Thread(()->{ reenterLock_DaMen(); }).start(); } private static void reenterLock_DaMen() { synchronized (ReenterLockTest_Phone.class){ System.out.println(Thread.currentThread().getName()+"打开大门"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } reenterLock_room(); } } private static void reenterLock_room() { synchronized (ReenterLockTest_Phone.class){ System.out.println(Thread.currentThread().getName()+"打开房间门"); } } }
自旋锁
底层原理
缺点
循环会耗时
只能一个自变量的原子性
ABA
import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicStampedReference; public class ABATest { public static void main(String[] args) { //ABA(); resolveABA(); } /** * "D:\Program Files\Java\jdk1.8.0_131\bin\java.exe" "-javaagent:D:\Program Files\IntelliJ IDEA 2019.3.4\lib\idea_rt.jar=51279:D:\Program Files\IntelliJ IDEA 2019.3.4\bin" -Dfile.encoding=UTF-8 -classpath "D:\Program Files\Java\jdk1.8.0_131\jre\lib\charsets.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\deploy.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\access-bridge-64.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\cldrdata.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\dnsns.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jaccess.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\jfxrt.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\localedata.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\nashorn.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunec.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunjce_provider.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunmscapi.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\sunpkcs11.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\ext\zipfs.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\javaws.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jce.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jfr.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jfxswt.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\jsse.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\management-agent.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\plugin.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\resources.jar;D:\Program Files\Java\jdk1.8.0_131\jre\lib\rt.jar;E:\coding\java1.0\target\classes;E:\coding\java1.0\src\main\java\lib\commons-io-2.6.jar;C:\Users\86157\.m2\repository\org\projectlombok\lombok\1.18.24\lombok-1.18.24.jar;C:\Users\86157\.m2\repository\junit\junit\4.12\junit-4.12.jar;C:\Users\86157\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.fh.thread.locks.spinlock.ABATest * A1 = >1 * B1 = >1 * b2 = >true * A2 = >false * b2 = >值:6;版本号:2 * A2 = >值:6;版本号:2 * A3 = >false * A3 = >值:6;版本号:2 */ private static void resolveABA() { AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1, 1);//初始值,版本号时间戳 new Thread(() -> { int stamp = atomicInteger.getStamp();//获得版本号 System.out.println("A1 = >" + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } //最后两个参数,拿到最新的版本号,把版本号+1 System.out.println("A2 = >" + atomicInteger.compareAndSet(1, 2, atomicInteger.getStamp(), atomicInteger.getStamp() + 1)); System.out.println("A2 = >值:" + atomicInteger.getReference() + ";版本号:" + atomicInteger.getStamp()); //把这个值该回去 System.out.println("A3 = >" + atomicInteger.compareAndSet(2, 1, atomicInteger.getStamp(), atomicInteger.getStamp() + 1)); System.out.println("A3 = >值:" + atomicInteger.getReference() + ";版本号:" + atomicInteger.getStamp()); }, "a").start(); new Thread(() -> { int stamp = atomicInteger.getStamp();//获得版本号 System.out.println("B1 = >" + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("b2 = >" + atomicInteger.compareAndSet(1, 6, stamp, stamp + 1)); System.out.println("b2 = >值:" + atomicInteger.getReference() + ";版本号:" + atomicInteger.getStamp()); }, "b").start(); } /** * 【1、原来的值1,更新为2】 * true * 2 * 【2、中间偷偷把2更新为原来的值1】 * true * 1 * 【3、因为第二步,把新值又更新为原来的值,所以此次以为原来的值没有更改过】 * true * 11 */ private static void ABA() { AtomicInteger atomicInteger = new AtomicInteger(1); System.out.println("【1、原来的值1,更新\"D:\\Program Files\\Java\\jdk1.8.0_131\\bin\\java.exe\" \"-javaagent:D:\\Program Files\\IntelliJ IDEA 2019.3.4\\lib\\idea_rt.jar=51279:D:\\Program Files\\IntelliJ IDEA 2019.3.4\\bin\" -Dfile.encoding=UTF-8 -classpath \"D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\charsets.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\deploy.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\access-bridge-64.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\cldrdata.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\dnsns.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\jaccess.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\jfxrt.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\localedata.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\nashorn.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\sunec.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\sunjce_provider.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\sunmscapi.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\sunpkcs11.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\ext\\zipfs.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\javaws.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\jce.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\jfr.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\jfxswt.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\jsse.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\management-agent.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\plugin.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\resources.jar;D:\\Program Files\\Java\\jdk1.8.0_131\\jre\\lib\\rt.jar;E:\\coding\\java1.0\\target\\classes;E:\\coding\\java1.0\\src\\main\\java\\lib\\commons-io-2.6.jar;C:\\Users\\86157\\.m2\\repository\\org\\projectlombok\\lombok\\1.18.24\\lombok-1.18.24.jar;C:\\Users\\86157\\.m2\\repository\\junit\\junit\\4.12\\junit-4.12.jar;C:\\Users\\86157\\.m2\\repository\\org\\hamcrest\\hamcrest-core\\1.3\\hamcrest-core-1.3.jar\" com.fh.thread.locks.spinlock.ABATest\n" + "A1 = >1\n" + "B1 = >1\n" + "b2 = >true\n" + "A2 = >false\n" + "b2 = >值:6;版本号:2\n" + "A2 = >值:6;版本号:2\n" + "A3 = >false\n" + "A3 = >值:6;版本号:2\n" + "\n" + "Process finished with exit code 0\n为2】"); System.out.println(atomicInteger.compareAndSet(1, 2)); System.out.println(atomicInteger.get()); System.out.println("【2、中间偷偷把2更新为原来的值1】"); System.out.println(atomicInteger.compareAndSet(2, 1)); System.out.println(atomicInteger.get()); System.out.println("【3、因为第二步,把新值又更新为原来的值,所以此次以为原来的值没有更改过】"); System.out.println(atomicInteger.compareAndSet(1, 11)); System.out.println(atomicInteger.get()); } }
自定义AtomicXXX
package com.fh.thread.locks.spinlock; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; public class SpinLockTest { public static void main(String[] args) { SpinLock spinLock = new SpinLock(); new Thread(()->{ try { spinLock.lock(); TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }finally { spinLock.unlock(); } },"t1").start(); new Thread(()->{ try { spinLock.lock(); TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }finally { spinLock.unlock(); } },"t2").start(); } } class SpinLock{ private AtomicReference<Thread> atomicReference = new AtomicReference<>(); public void lock(){ Thread thread = Thread.currentThread(); System.out.println(thread.getName()+"加锁"); while (!atomicReference.compareAndSet(null,thread)){ try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(thread.getName()+"自旋吧。。。"); } } public void unlock(){ Thread thread = Thread.currentThread(); System.out.println(thread.getName()+"解锁"); atomicReference.compareAndSet(thread,null); } }
死锁
public class DeadLockTest { public static void main(String[] args) { String lockA = "lockA"; String lockB = "lockB"; new Thread(new MyThread(lockA,lockB),"1线程").start(); new Thread(new MyThread(lockB,lockA),"2线程").start(); } } class MyThread implements Runnable{ private String lock1; private String lock2; public MyThread(String lock1, String lock2) { this.lock1 = lock1; this.lock2 = lock2; } @Override public void run() { synchronized (lock1){ //lockA想拿B System.out.println(Thread.currentThread().getName()+ lock1 + "=> get" + lock2); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lock2){ //B想拿A System.out.println(Thread.currentThread().getName() + lock2 + "=> get" + lock1); } } } }
死锁问题排查
乐观锁
CAS是乐观锁的技术实现
悲观锁
它总是会假设当前情况是最坏的情况,在每次去拿数据的时候,都会认为数据会被别人改变,因此在每次进行拿数据操作的时候都会加锁,如此一来,如果此时有别人也来拿这个数据的时候就会阻塞知道它拿到锁。在Java中,Synchronized和ReentrantLock等独占锁的实现机制就是基于悲观锁思想。在数据库中也经常用到这种锁机制,如行锁,表锁,读写锁等,都是在操作之前先上锁,保证共享资源只能给一个操作(一个线程)使用。
由于悲观锁的频繁加锁,因此导致了一些问题的出现:比如在多线程竞争下,频繁加锁、释放锁导致频繁的上下文切换和调度延时,一个线程持有锁会导致其他线程进入阻塞状态,从而引起性能问题。