19、深入理解CAS
1)什么是CAS
深入研究底层!!!
package com.kuang.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
//CAS
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//compareAndSet:比较并交换!!
/**
* 期望、更新
* public final boolean compareAndSet(int expect, int update) {
* return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
* }
*
* CAS:是CPU的并发原语
*/
System.out.println(atomicInteger.compareAndSet(2020, 2021));
// 如果我期望的值达到了就更新,否则就不更新
System.out.println(atomicInteger.get());
// atomicInteger.getAndIncrement();
/**
* public final int getAndIncrement() {
* return unsafe.getAndAddInt(this, valueOffset, 1);
* }
*/
System.out.println(atomicInteger.compareAndSet(2020, 2021));
// 如果我期望的值达到了就更新,否则就不更新
System.out.println(atomicInteger.get());
}
}
2) Unsafe类
atomicInteger.getAndIncrement();
CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作!如果不是就一直循环!
缺点:
1、循环会耗时
2、一次性只能保证一个共享变量的原子性
3、ABA问题
3)CAS:ABA 问题
狸猫换太子
package com.kuang.cas;
import java.util.concurrent.atomic.AtomicInteger;
public class CASDemo {
//CAS
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//compareAndSet:比较并交换!!
/**
* 期望、更新
* public final boolean compareAndSet(int expect, int update) {
* return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
* }
*
* CAS:是CPU的并发原语
*/
// 对于我们平时写的SQL:乐观锁
System.out.println(atomicInteger.compareAndSet(2020, 2021));
// 如果我期望的值达到了就更新,否则就不更新
// CAS 是CPU的并发原语!
System.out.println(atomicInteger.get());
// atomicInteger.getAndIncrement();
/**
* public final int getAndIncrement() {
* return unsafe.getAndAddInt(this, valueOffset, 1);
* }
*/
System.out.println(atomicInteger.compareAndSet(2021, 2020));
System.out.println(atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(2020, 6666));
System.out.println(atomicInteger.get());
}
}
20、原子引用
1) 解决ABA问题,引入原子引用!
对应的思想:乐观锁!
带版本号的原子操作
package com.kuang.cas;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
public class Test {
// AtomicStampedReference 注意,如果泛型是包装类,注意对象的引用问题
static AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1, 1);
public static void main(String[] args) {
new Thread(()->{
int stamp = atomicInteger.getStamp();//获得版本号
System.out.println("a1="+stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//version+1
atomicInteger.compareAndSet(1, 2,
atomicInteger.getStamp(), atomicInteger.getStamp() + 1);
System.out.println("a2="+atomicInteger.getStamp());
System.out.println(atomicInteger.compareAndSet(2, 1,
atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("a3="+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(atomicInteger.compareAndSet(1, 6,
stamp, stamp + 1));
System.out.println("b2="+atomicInteger.getStamp());
},"b").start();
}
}
21、各种锁的理解
1)公平锁、非公平锁
公平锁:非常公平,不能够插队,必须先来后到!
非公平锁:非常不公平,可以插队(默认都是非公平的)
2)可重入锁
可重入锁(递归锁)
package com.kuang.lock;
// synchronized
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();// 这里也有锁
}
public synchronized void call(){
System.out.println(Thread.currentThread().getName()+"call");
}
}
package com.kuang.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();// 这里也有锁
}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();
}}
}
3)自旋锁
spinlock
package com.kuang.lock;
import jdk.nashorn.internal.ir.CallNode;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;
//自旋锁
public class SpinlockDemo {
//int 0
//Thread null
AtomicReference<Thread> atomicReference=new AtomicReference<>();
// 加锁
public void myLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"==>mylock");
//自旋锁
while(!atomicReference.compareAndSet(null,thread)){
}
}
// 解锁
public void myUnLock(){
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"==>myUnlock");
atomicReference.compareAndSet(thread,null);
}
}
测试:
package com.kuang.lock;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
public class TestSpinlock {
public static void main(String[] args) throws InterruptedException {
// ReentrantLock reentrantLock = new ReentrantLock();
// reentrantLock.lock();
// reentrantLock.unlock();
//最底层使用了自旋锁
SpinlockDemo spinlockDemo = new SpinlockDemo();
new Thread(()->{
spinlockDemo.myLock();
try{
TimeUnit.SECONDS.sleep(3);
}catch (Exception e){
e.printStackTrace();
}finally {
spinlockDemo.myUnLock();
}
},"a").start();
TimeUnit.SECONDS.sleep(1);
new Thread(()->{
spinlockDemo.myLock();
try{
TimeUnit.SECONDS.sleep(1);
}catch (Exception e){
e.printStackTrace();
}finally {
spinlockDemo.myUnLock();
}
},"b").start();
}
}
运行结果:
4)死锁
1、死锁是什么?
2、死锁的排除
package com.kuang.lock;
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;
@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);
}
}
}
public MyThread(String lockA,String lockB){
this.lockA=lockA;
this.lockB=lockB;
}
}
3、解决问题
①使用 jps -l 定位进程号
②使用 jstack 进程号 查看进程信息