Java多线程 JUC并发编程 (第三天)

多线程进阶=>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 进程号 找到死锁问题

在这里插入图片描述
  找到最后一行,查看具体死锁的代码

在这里插入图片描述


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值