多线程进阶=>JUC并发编程
19、深入理解CAS
19.1、什么是 CAS
代码实现:
package com.feimai.test.JUCTest.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class Test {
// CAS compareAndSet : 比较并交换!
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2021);
// 期望、更新
// public final boolean compareAndSet(int expect, int update)
// 如果我期望的值达到了,那么就更新,否则,就不更新, CAS 是CPU的并发原语!
System.out.println(atomicInteger.compareAndSet(2021, 2022));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2021, 2022));
System.out.println(atomicInteger.get());
atomicInteger.getAndIncrement();
}
}
19.2、Unsafe 类
CAS:比较当前工作内存中的值,和主内存中的值,如果这个值是期望的,那么执行操作,如果不是就一直循环
缺点:
1、循环会耗时
2、一次性只能保证一个共享变量的原子性
3、ABA问题
19.3、ABA 问题
代码实现:
package com.feimai.test.JUCTest.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class Test {
// CAS compareAndSet : 比较并交换!
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2021);
// 期望、更新
// public final boolean compareAndSet(int expect, int update)
// 如果我期望的值达到了,那么就更新,否则,就不更新, CAS 是CPU的并发原语!
// ============== 捣乱的线程 ==================
System.out.println(atomicInteger.compareAndSet(2021, 2022));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2022, 2021));
System.out.println(atomicInteger.get());
// ============== 期望的线程 ==================
System.out.println(atomicInteger.compareAndSet(2021, 2022));
System.out.println(atomicInteger.get());
atomicInteger.getAndIncrement();
}
}
20、原子引用
解决ABA 问题,引入原子引用! 对应的思想:乐观锁!
带版本号 的原子操作!
代码实现:
package com.feimai.test.JUCTest.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
public class Test02 {
public static void main(String[] args) {
//AtomicStampedReference 注意,如果泛型是一个包装类,注意对象的引用问题
// 正常在业务操作,这里面比较的都是一个个对象
AtomicStampedReference<Long> atomicStampedReference = new AtomicStampedReference(2L,1);
// CAS compareAndSet : 比较并交换!
new Thread(()->{
System.out.println("a1 = " + atomicStampedReference.getStamp());// 获得版本号
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(2L, 5L,
atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("a2 = " + atomicStampedReference.getStamp());
System.out.println(atomicStampedReference.compareAndSet(5L, 2L,
atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("a3 = " + atomicStampedReference.getStamp());
},"A").start();
// 乐观锁的原理相同!
new Thread(()->{
int stamp = atomicStampedReference.getStamp();
System.out.println("b1 = " + stamp); // 获得版本号
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(2L, 5L,
stamp, stamp + 1));
System.out.println("b2 = " + atomicStampedReference.getStamp());
},"B").start();
}
}
注意:
Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实例,而不是 new,因为 valueOf 使用缓存,而 new 一定会创建新的对象分配新的内存空间;
21、各种锁的理解
21.1、公平锁、非公平锁
公平锁: 非常公平, 不能够插队,必须先来后到!
非公平锁:非常不公平,可以插队 (默认都是非公平)
ReentrantLock 锁的源码分析:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
21.2、可重入锁
Synchronized版 代码:
package com.feimai.test.JUCTest.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
// Synchronized
public class Test01 {
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();
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName() + " : call");
}
}
Lock版 代码:
package com.feimai.test.JUCTest.lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test02 {
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(); lock.unlock();
// lock 锁必须配对,否则就会死在里面
Lock lock = new ReentrantLock();
public void sms(){
lock.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();
}
}
}
21.3、 自旋锁
SpinLock
自定义一个 自旋锁,代码:
package com.feimai.test.JUCTest.lock;
import java.sql.Time;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
// 自旋锁
public class SpinLockTest {
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 static void main(String[] args) throws InterruptedException {
// ReentrantLock reentrantLock = new ReentrantLock();
// reentrantLock.lock();
// reentrantLock.unlock();
// 底层使用的自旋锁CAS
SpinLockTest lockTest = new SpinLockTest();
new Thread(()->{
lockTest.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (Exception e) {
e.printStackTrace();
} finally {
lockTest.myUnlock();
}
},"A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
lockTest.myLock();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
} finally {
lockTest.myUnlock();
}
},"B").start();
}
}
21.4、死锁
死锁是什么
死锁测试,怎么排除死锁:
package com.feimai.test.JUCTest.lock;
import java.util.concurrent.TimeUnit;
public class DeadLockTest {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
MyThread myThread1 = new MyThread(lockA,lockB);
MyThread myThread2 = new MyThread(lockB,lockA);
new Thread(myThread1,"T1").start();
new Thread(myThread2,"T2").start();
}
}
class MyThread implements Runnable{
private String lock_A;
private String lock_B;
public MyThread(String lock_A, String lock_B) {
this.lock_A = lock_A;
this.lock_B = lock_B;
}
@Override
public void run() {
synchronized(lock_A){
System.out.println(Thread.currentThread().getName() +" lock: "+lock_A+" -->get: "+lock_B);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(lock_B){
System.out.println(Thread.currentThread().getName() +" lock: "+lock_B+" -->get: "+lock_A);
}
}
}
}
解决问题:
1、使用 jps -l
定位进程号
2、使用 jstack 进程号
找到死锁问题
找到最后一行,查看具体死锁的代码