JAVA并发编程之锁

  • Post author:
  • Post category:java




1、乐观锁和悲观锁



1.1、悲观锁

  1. 认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会加锁,确保数据不会别的线程修改。

  2. synchronized关键字和Lock的实现类都是悲观锁。
  3. 适合写操作多的场景,先加锁可以保证写操作时数据正确,显示的锁定之后再造作同步资源。
  4. 狼性锁



1.2、乐观锁

  1. 认为自己在使用数据时不会有别的线程修改数据或资源,所以不会添加锁。

  2. 在JAVA中是通过使用无锁编程来实现,只是在更新数据的时候去判断,之前有没有别的线程更新了这个数据。如果这个数据没有被更新,当前线程将自己修改的数据成功写入。如果这个数据已经被其它线程更新,则根据不同的实现方式执行不同的操作,比如放弃修改、重试抢锁等等。

  3. 判断规则:

    (1)版本号机制Version

    (2)

    最常采用的是CAS算法,JAVA原子类中的递增操作就通过CAS自旋实现的。

  4. 适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅度提升。

  5. 乐观锁直接去操作同步资源,是一种无锁算法,得之我幸不得我命,再努力就是一句话:佛系锁。

    在这里插入图片描述



2、线程八锁

  1. 标准访问有a、b两个线程,请问先打印邮件还是短信?
import java.util.concurrent.TimeUnit;

/**
 * @Description: 线程8锁
 * @Author: yangyb
 * @Date:2022/9/28 22:25
 * Version: 1.0
 **/

class Phone{
    public synchronized void sendEmail(){
        System.out.println("-------------sendEmail");
    }

    public synchronized void sendSMS(){
        System.out.println("-----------sendSMS");
    }
}

public class Lock8Demo {

    public static void main(String[] args){
        Phone phone = new Phone();

        new Thread(phone::sendEmail,"a").start();

        //暂停两毫秒,保证a线程先启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(phone::sendSMS,"b").start();
    }

}

synchronized 加在普通的方法上面,是对象锁,由于a线程先启动,在调用sendEmail()方法时先拿到了这个phone对象的锁,所以肯定先执行此方法,此方法执行完后,a线程释放phone对象锁,然后b线程才能拿到phone对象锁,去执行sendSMS()方法。

在这里插入图片描述

2. sendEmail方法中加入暂停3秒钟,请问先打印邮件还是hello?

import java.util.concurrent.TimeUnit;

/**
 * @Description: 线程8锁
 * @Author: yangyb
 * @Date:2022/9/28 22:25
 * Version: 1.0
 **/

class Phone{
    public synchronized void sendEmail(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------------sendEmail");
    }

    public synchronized void sendSMS(){
        System.out.println("-----------sendSMS");
    }
}

public class Lock8Demo {

    public static void main(String[] args){
        Phone phone = new Phone();

        new Thread(phone::sendEmail,"a").start();

        //暂停两毫秒,保证a线程先启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(phone::sendSMS,"b").start();
    }

}

synchronized 加在普通的方法上面,是对象锁,由于a线程先启动,在调用sendEmail()方法时先拿到了这个phone对象的锁,此时虽然暂停了3秒钟,但phone对象锁一直被a线程持有,所以肯定还是先执行sendEmail()方法,此方法执行完后,a线程释放phone对象锁,然后b线程才能拿到phone对象锁,去执行sendSMS()方法。

在这里插入图片描述

在这里插入图片描述

  1. 添加一个普通的hello方法,请问先打印邮件还是hello?
import java.util.concurrent.TimeUnit;

/**
 * @Description: 线程8锁
 * @Author: yangyb
 * @Date:2022/9/28 22:25
 * Version: 1.0
 **/

class Phone{
    public synchronized void sendEmail(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------------sendEmail");
    }

    public synchronized void sendSMS(){
        System.out.println("-----------sendSMS");
    }

    public void hello(){
        System.out.println("----------hello");
    }
}

public class Lock8Demo {

    public static void main(String[] args){
        Phone phone = new Phone();

        new Thread(phone::sendEmail,"a").start();

        //暂停两毫秒,保证a线程先启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(phone::hello,"b").start();
    }

}

a线程拿到phone对象锁以后,去执行sendEmail()方法,但中间暂停了3秒钟,同时b线程执行的是普通的hello(),所以不需要获取phone对象锁,就可以立即执行hello()方法,而不用等到a线程释放phone对象锁,再去获取phone对象锁。

在这里插入图片描述

  1. 有两个手机对象,请问先打印邮件还是短信?
/**
 * @Description: 线程8锁
 * @Author: yangyb
 * @Date:2022/9/28 22:25
 * Version: 1.0
 **/

class Phone{
    public synchronized void sendEmail(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------------sendEmail");
    }

    public synchronized void sendSMS(){
        System.out.println("-----------sendSMS");
    }

    public void hello(){
        System.out.println("----------hello");
    }
}

public class Lock8Demo {

    public static void main(String[] args){
        Phone phone = new Phone();
        Phone phoneOne = new Phone();

        new Thread(phone::sendEmail,"a").start();

        //暂停两毫秒,保证a线程先启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(phoneOne::sendSMS,"b").start();
    }

}

a线程持有的是phone对象的锁,并不会影响到b线程获取phoneOne对象的锁,所以是两个线程拿到了两个不同的对象锁,互不干扰,各自执行各自的。

在这里插入图片描述

在这里插入图片描述

  1. 有两个静态同步方法,有一个手机对象,请问先打印邮件还是短信?
import java.util.concurrent.TimeUnit;

/**
 * @Description: 线程8锁
 * @Author: yangyb
 * @Date:2022/9/28 22:25
 * Version: 1.0
 **/

class Phone{
    public static synchronized void sendEmail(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------------sendEmail");
    }

    public static synchronized void sendSMS(){
        System.out.println("-----------sendSMS");
    }

    public void hello(){
        System.out.println("----------hello");
    }
}

public class Lock8Demo {

    public static void main(String[] args){
        Phone phone = new Phone();

        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        //暂停两毫秒,保证a线程先启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.sendSMS();
        },"b").start();
    }

}

静态方法属于类,在静态方法上加上synchronized关键字,线程a先启动获得的就是类锁,这时候锁住的是整个类的资源,所以线程b必须等待线程a释放类锁以后才能获取到类锁。

在这里插入图片描述

6. 有两个静态同步方法,有两个手机对象,请问先打印邮件还是短信?

import java.util.concurrent.TimeUnit;

/**
 * @Description: 线程8锁
 * @Author: yangyb
 * @Date:2022/9/28 22:25
 * Version: 1.0
 **/

class Phone{
    public static synchronized void sendEmail(){
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------------sendEmail");
    }

    public static synchronized void sendSMS(){
        System.out.println("-----------sendSMS");
    }

    public void hello(){
        System.out.println("----------hello");
    }
}

public class Lock8Demo {

    public static void main(String[] args){
        Phone phone = new Phone();
        Phone phoneOne = new Phone();
        new Thread(()->{
            phone.sendEmail();
        },"a").start();

        //暂停两毫秒,保证a线程先启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phoneOne.sendSMS();
        },"b").start();
    }

}

静态方法属于类,在静态方法上加上synchronized关键字,线程a先启动获得的就是类锁,这时候锁住的是整个类的资源,所以线程b必须等待线程a释放类锁以后才能获取到类锁,即使创建了两个不同的对象,但这两个对象都属于同一个类,所以b线程还是要等待a线程释放整个类锁后,才能获得类锁,再执行响应的资源。

在这里插入图片描述

在这里插入图片描述

  1. 有一个静态同步方法,有一个普通同步方法,有一个手机对象,请问先打印邮件还是短信?
import java.util.concurrent.TimeUnit;

/**
 * @Description: 线程8锁
 * @Author: yangyb
 * @Date:2022/9/28 22:25
 * Version: 1.0
 **/

class Phone {
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------------sendEmail");
    }

    public synchronized void sendSMS() {
        System.out.println("-----------sendSMS");
    }

    public void hello() {
        System.out.println("----------hello");
    }
}

public class Lock8Demo {

    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "a").start();

        //暂停两毫秒,保证a线程先启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phone.sendSMS();
        }, "b").start();
    }

}

获取静态同步方法,需要持有类锁,普通同步方法,获取的是对象锁,所以b线程不需要等到a线程释放完类锁,就能能获取到对象锁。

在这里插入图片描述

  1. 有一个静态同步方法,有一个普通同步方法,有两个手机对象,请问先打印邮件还是短信?
import java.util.concurrent.TimeUnit;

/**
 * @Description: 线程8锁
 * @Author: yangyb
 * @Date:2022/9/28 22:25
 * Version: 1.0
 **/

class Phone {
    public static synchronized void sendEmail() {
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-------------sendEmail");
    }

    public synchronized void sendSMS() {
        System.out.println("-----------sendSMS");
    }

    public void hello() {
        System.out.println("----------hello");
    }
}

public class Lock8Demo {

    public static void main(String[] args) {
        Phone phone = new Phone();
        Phone phoneOne = new Phone();
        new Thread(() -> {
            phone.sendEmail();
        }, "a").start();

        //暂停两毫秒,保证a线程先启动
        try {
            TimeUnit.MILLISECONDS.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            phoneOne.sendSMS();
        }, "b").start();
    }

}

获取静态同步方法,需要持有类锁,普通同步方法,获取的是对象锁,所以b线程不需要等到a线程释放完类锁,就能能获取到对象锁。

在这里插入图片描述

在这里插入图片描述



3、公平锁和非公平锁



3.1、非公平锁

import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description: 非公平锁
 * @Author: yangyb
 * @Date:2022/10/5 11:38
 * Version: 1.0
 **/
class Ticket {
    private int number = 20;
    // 默认为非公平锁
    ReentrantLock lock = new ReentrantLock();

    public void sale() {
        lock.lock();
        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "售出了第" + number-- + "张票,还剩" + number + "票");
            }
        } finally {
            lock.unlock();
        }

    }
}

public class SaleTicketDemo {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(() -> {
            for (int i = 0; i < 55; i++) {
                ticket.sale();
            }
        }, "a").start();
        new Thread(() -> {
            for (int i = 0; i < 55; i++) {
                ticket.sale();
            }
        }, "b").start();
        new Thread(() -> {
            for (int i = 0; i < 55; i++) {
                ticket.sale();
            }
        }, "c").start();
    }

}

在这里插入图片描述



3.2、公平锁

import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description: 公平锁
 * @Author: yangyb
 * @Date:2022/10/5 11:38
 * Version: 1.0
 **/
class Ticket {
    private int number = 20;
    // 公平锁
    ReentrantLock lock = new ReentrantLock(true);

    public void sale() {
        lock.lock();
        try {
            if (number > 0) {
                System.out.println(Thread.currentThread().getName() + "售出了第" + number-- + "张票,还剩" + number + "票");
            }
        } finally {
            lock.unlock();
        }

    }
}

public class SaleTicketDemo {

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        new Thread(() -> {
            for (int i = 0; i < 22; i++) {
                ticket.sale();
            }
        }, "a").start();
        new Thread(() -> {
            for (int i = 0; i < 22; i++) {
                ticket.sale();
            }
        }, "b").start();
        new Thread(() -> {
            for (int i = 0; i < 22; i++) {
                ticket.sale();
            }
        }, "c").start();
    }

}

在这里插入图片描述

在这里插入图片描述



3.3、为什么会有公平锁和非公平锁的设计?为什么默认非公平?

  1. 恢复挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从cpu的角度来看,这个时间差存在的还是很明显的。所以非公平锁能更充分的利用CPU的时间片,尽量减少cpu空闲状态时间。
  2. 使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时,当1个线程请求锁获取同步状态,然后释放同步状态,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得非常大,所以就减少了线程的开销。



3.4、何时用公平锁?何时用非公平锁?

如果为了更高的吞吐量,很显然非公平锁是比较合适的,因为节省很多线程切换时间,吞吐量自然就上去了;否则就用公平锁,大家公平使用。



4、可重入锁(又名递归锁)



4.1、概念

  • 指同一个线程在外层方法获取锁的时候,再进入该线程的内层方法会自动获取锁(前提,锁对象得是同一个对象),不会因为之前已经获取过还没释放而阻塞。

    例如:如果是一个有symchronized修饰得递归调用方法,程序第二次进入被自己阻塞了,这不成自己阻塞自己了吗?显然就不合常规的处理逻辑了。所以Java中ReentrantLock和synchronized都是可重入锁,可重入锁的一个优点是可以一定程度避免死锁。
  • 总结:一个线程中的多个流程可以获取同一把锁,持有这把同步锁可以再次进入。即自己可以获取自己的内部锁。



4.2、隐式锁

隐式锁(即synchronized关键字使用的锁)默认是可重入锁。指的是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫可重入锁。简单的lai说就是,在一个synchronized修饰的方法或代码块的内部调用本类的其他synchronized修饰的方法或代码块时,是永远可以得到锁的。

/**
 * @Description: 隐式锁synchronized,同步代码块
 * @Author: yangyb
 * @Date:2022/10/6 19:08
 * Version: 1.0
 **/
public class ReEntryLockDemo {

    public static void main(String[] args) {
        Object object = new Object();

        new Thread(() -> {
            synchronized (object) {
                System.out.println(Thread.currentThread().getName() + "\t-------外层调用");
                synchronized (object) {
                    System.out.println(Thread.currentThread().getName() + "\t-------中层调用");
                    synchronized (object) {
                        System.out.println(Thread.currentThread().getName() + "\t-------内层调用");
                    }
                }
            }
        }, "t1").start();
    }

}

在这里插入图片描述

/**
 * @Description: 隐式锁synchronized,同步方法
 * @Author: yangyb
 * @Date:2022/10/6 19:08
 * Version: 1.0
 **/
public class ReEntryLockDemo {

    public synchronized void m1(){
        System.out.println("---m1 "+Thread.currentThread().getName()+"\t ------come in!");
        m2();
        System.out.println("---m1 "+Thread.currentThread().getName()+"\t ------end m1!");
    }

    public synchronized void m2(){
        System.out.println("---m2 "+Thread.currentThread().getName()+"\t ------come in!");
        m3();
    }

    public synchronized void m3(){
        System.out.println("---m3 "+Thread.currentThread().getName()+"\t ------come in!");
    }

    public static void main(String[] args) {

        ReEntryLockDemo reEntryLockDemo = new ReEntryLockDemo();
        new Thread(reEntryLockDemo::m1,"t1").start();

    }

}

在这里插入图片描述

4.2.1、Synchronized可重入的实现原理

在这里插入图片描述



4.3、显示锁(ReentrantLock)

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description: 显示锁ReentrantLock
 * @Author: yangyb
 * @Date:2022/10/6 19:08
 * Version: 1.0
 **/
public class ReEntryLockDemo {

    static Lock lock = new ReentrantLock();

    public static void main(String[] args) {

        new Thread(() -> {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "\t 外层调用lock!");
                lock.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + "\t 内层调用lock!");
                } finally {
                    // 这里故意注释掉,让实现加锁次数和释放锁的次数不一样
                    // 由于枷锁次数和释放锁次数不一样,第二个线程始终无法获取到锁,导致一直在等待
                    //lock.unlock();
                }
            } finally {
                lock.unlock();
            }
        }, "t1").start();

        new Thread(() -> {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "\t 调用lock!");
            } finally {
                lock.unlock();
            }
        }, "t2").start();
    }
}

在这里插入图片描述



5、死锁及排查



5.1、死锁

5.1.1、概念

在这里插入图片描述

5.1.2、代码示例

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @Description: 死锁
 * @Author: yangyb
 * @Date:2022/10/6 20:19
 * Version: 1.0
 **/
public class DeadlockDemo {

    static Lock lockA = new ReentrantLock();

    static Lock lockB = new ReentrantLock();

    public static void main(String[] args) {

        new Thread(() -> {
            lockA.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "\t 线程持有lockA锁,去尝试持有lockB锁");
                lockB.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + "\t 尝试获取并持有了lockB锁");
                } finally {
                    lockB.unlock();
                }

            } finally {
                lockA.unlock();
            }
        }, "A").start();

        new Thread(() -> {
            lockB.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "\t 线程持有lockB锁,去尝试持有lockA锁");
                lockA.lock();
                try {
                    System.out.println(Thread.currentThread().getName() + "\t 尝试获取并持有了lockA锁");
                } finally {
                    lockA.unlock();
                }

            } finally {
                lockB.unlock();
            }
        }, "B").start();

    }
}

在这里插入图片描述

5.1.3、产生死锁的主要原因

  • 系统资源不足
  • 进程运行推进的顺序不合适
  • 资源分配不当



5.2、死锁排查

5.2.1、在终端使用命令行


  1. jps -l


    在这里插入图片描述


  2. jstack 17988


    在这里插入图片描述

    在这里插入图片描述

5.2.2、图形化

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



6、为什么任何一个对象都可以成为锁?

在这里插入图片描述

在这里插入图片描述



7、LockSupport与线程中断



7.1、线程中断

7.1.1、什么是中断机制?

首先,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止,自己来决定自己的命运。所以,Thread.stop,Thread.suspend,Thread.resume都已经被废弃了。

其次,在java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的协商机制——中断,即中断标识协商机制。

中断只是一种协作协商机制,Java中没有给中断增加任何语法,中断的过程完全需要程序员自己实现。

若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线程对象的中断标识设成true,此时究竟该做什么需要你自己写代码实现。

每个线程对象中都有一个中断标识位,用于表示 线程是否被中断;该标识位为true表示中断,为false表示未中断;通过调用线程对象的interrupt方法将该线程的标识位设为true;可以在别的线程中调用,也可以在自己的线程中调用。

7.1.2、中断线程API常见的三大方法

在这里插入图片描述

  1. 如何停止中断允许中的线程?

    (1)同过一个volatile 变量实现
/**
 * @Description: 线程中断
 * @Author: yangyb
 * @Date:2022/10/7 1:40
 * Version: 1.0
 **/
public class InterruptedDemo {

    static volatile boolean isStop = false;

    public static void main(String[] args) {
        new Thread(() -> {
            while (true) {
                if (isStop) {
                    System.out.println(Thread.currentThread().getName() + "\t isStop被修改为" + isStop + "线程终止!");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "\t 线程正在执行!");
            }
        }, ":t1").start();
        try {
            TimeUnit.MILLISECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            isStop = true;
        }, "t2").start();
    }

}

在这里插入图片描述

(2)同过AtomicBoolean 实现

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Description: 线程中断
 * @Author: yangyb
 * @Date:2022/10/7 1:40
 * Version: 1.0
 **/
public class InterruptedDemo {

    static AtomicBoolean atomicBoolean=new AtomicBoolean(false);

    public static void main(String[] args) {

        new Thread(() -> {
            while (true) {
                if (atomicBoolean.get()) {
                    System.out.println(Thread.currentThread().getName() + "\t atomicBoolean被修改为" + atomicBoolean.get() + "线程终止!");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "\t 线程正在执行!");
            }
        }, ":t1").start();
        try {
            TimeUnit.MILLISECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(() -> {
            atomicBoolean.set(true);
        }, "t2").start();

    }

}

在这里插入图片描述

(3)通过Thread类自带的pai来实现

import java.util.concurrent.TimeUnit;

/**
 * @Description: 线程中断
 * @Author: yangyb
 * @Date:2022/10/7 1:40
 * Version: 1.0
 **/
public class InterruptedDemo {

    public static void main(String[] args) {

        Thread t1=new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() + "\t isInterrupted()被修改为" + true + "线程终止!");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "\t 线程正在执行!");
            }
        }, ":t1");
        t1.start();
        try {
            TimeUnit.MILLISECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        new Thread(t1::interrupt, "t2").start();

    }

}

在这里插入图片描述

源码分析:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2. 当前线程的中断标识为true,是不是线程就立刻停止?

import java.util.concurrent.TimeUnit;

/**
 * @Description: 线程中断
 * @Author: yangyb
 * @Date:2022/10/7 1:40
 * Version: 1.0
 **/
public class InterruptedDemo {

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 300; i++) {
                System.out.println("i=" + i);
            }
            System.out.println(Thread.currentThread().getName() + "\t 线程调用t1.interrupt()方法后的中断标识为" + Thread.currentThread().isInterrupted());
        }, ":t1");
        t1.start();
        System.out.println(t1.getName() + "\t 线程中默认的中断标识为" + t1.isInterrupted());//false
        try {
            TimeUnit.MILLISECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t1.interrupt();//true
        System.out.println(Thread.currentThread().getName() + "\t 线程调用t1.interrupt()方法后的第一次中断标识为" + t1.isInterrupted());

        try {
            TimeUnit.MILLISECONDS.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 中断标识重置为false,中断不活动的线程不会产生任何影响
        System.out.println(Thread.currentThread().getName() + "\t 线程调用t1.interrupt()方法,并等待t1线程执行完所有的逻辑后中断标识为" + t1.isInterrupted());
    }

}

在这里插入图片描述

在这里插入图片描述


此时t1线程的中断标识已经置为true,然而并没有立即中断线程,而是继续执行了所有逻辑


在这里插入图片描述


注意:


在这里插入图片描述


总结:


中断只是一种协商机制,修改中断标识位仅此而已,不是立刻stop打断。

  1. 静态方法Thread.interrupted()谈谈你的理解?

    在这里插入图片描述

    在这里插入图片描述

    在这里插入图片描述

    示例:

    在这里插入图片描述


结论:


静态方法interrupted将会清除中断状态(传入的参数ClearInterrupted为true),实例方法isInterrupted则不会(传入的参数ClearInterrupted为false)。

4.三大方法对比总结

在这里插入图片描述



7.2、LockSupport

7.2.1、概念

LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,当然阻塞之后肯定得有唤醒的方法。归根结底,LockSupport调用的Unsafe中的native代码。


LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。LockSupport 提供park()和unpark()方法实现阻塞线程和解除线程阻塞

,LockSupport和每个使用它的线程都有一个许可(permit)关联。permit相当于1,0的开关,默认是0,调用一次unpark就加1变成1,调用一次park会消费permit, 也就是将1变成0,同时park立即返回。再次调用park会变成block(因为permit为0了,会阻塞在这里,直到permit变为1), 这时调用unpark会把permit置为1。每个线程都有一个相关的permit,

permit最多只有一个,重复调用unpark也不会积累。

park() 和 unpark()不会有 Thread.suspend 和 Thread.resume 所可能引发的死锁问题,由于许可的存在,调用 park 的线程和另一个试图将其 unpark 的线程之间的竞争将保持活性。

如果调用线程被中断,则park方法会返回。同时park也拥有可以设置超时时间的版本。

在这里插入图片描述

7.2.2、线程等待唤醒机制

  1. 线程等待和唤醒的方法
    在这里插入图片描述

    (1)Object的wait和notify
    在这里插入图片描述

    在这里插入图片描述

    (2)Condition的await()和signal()方法

在这里插入图片描述

在这里插入图片描述

(3)LockSupport的park()和unpark()方法

park()和unpark()顺序执行

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * @Description: LockSupport
 * @Author: yangyb
 * @Date:2022/10/7 7:38
 * Version: 1.0
 **/
public class LockSupportDemo {

    public static void main(String[] args){

        Thread t1=new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"\t 线程---come in!");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName()+"\t 线程---被唤醒");
        },"t1");
        t1.start();

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName()+"\t 线程发出通知给t1线程");
        },"t2").start();
    }
}

在这里插入图片描述

先执行unpark(),再执行unpark()

/**
 * @Description: LockSupport
 * @Author: yangyb
 * @Date:2022/10/7 7:38
 * Version: 1.0
 **/
public class LockSupportDemo {

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "\t 线程---come in!");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "\t 线程---被唤醒");
        }, "t1");
        t1.start();

        new Thread(() -> {
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName() + "\t 线程发出通知给t1线程");
        }, "t2").start();
    }
}

在这里插入图片描述

7.2.3、总结

在这里插入图片描述

在这里插入图片描述

7.2.3、面试题

在这里插入图片描述

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * @Description: LockSupport
 * @Author: yangyb
 * @Date:2022/10/7 7:38
 * Version: 1.0
 **/
public class LockSupportDemo {

    public static void main(String[] args) {

        Thread t1 = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "\t 线程---come in!");
            LockSupport.park();
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "\t 线程---被唤醒");
        }, "t1");
        t1.start();

        new Thread(() -> {
            LockSupport.unpark(t1);
            LockSupport.unpark(t1);
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName() + "\t 线程发出通知给t1线程");
        }, "t2").start();
    }
}

在这里插入图片描述



8、Java内存模型即JMM



8.1、计算机硬件存储体系

在这里插入图片描述

在这里插入图片描述



8.2、JMM

8.2.1、JMM简介

在这里插入图片描述

8.2.2、JMM规范下,三大特性

(1)可见性

指当一个线程修改了某一个共享变量的值,其他线程能否立即知道该变更。JMM规定所有的变量都存储在主内存中。

在这里插入图片描述

在这里插入图片描述

(2)原子性

指一个操作是不可打断的,即多线程环境下,操作不能被其他线程干扰。

(3)有序性

在这里插入图片描述

在这里插入图片描述

8.2.3、JMM规范下,多线程对变量的读写过程

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

8.2.3、JMM规范下,多线程先行发生原则之happens-before

(1)简介

在这里插入图片描述

(2)happens-before总原则

如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。

两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行最终的结果一致,那么这种重排序并不非法。

(2)happens-before的8条规则

  1. 次序规则:一个线程内,按照代码顺序,写在前面的操作先行发生于写在后面的操作。
  2. 锁定规则:一个unlock操作先行发生于后面(这里的“后面”是指时间上的先后)对同一个锁的lock操作。
  3. volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作,前面的写对后面的读是可见的,这里的“后面”同样是指时间上的先后。
  4. 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出来操作A先行发生于操作C。
  5. 线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作。
  6. 线程中断规则(Thread Interruption Rule):

    在这里插入图片描述
  7. 线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过isAlive()等手段检测线程是否已经终止执行。
  8. 对象终结规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。

    (4)总结

    在这里插入图片描述



9、volatile



9.1、volatile之两大特性


被volatile修饰的变量有两大特点:可见性和有序性

  1. 可见性:保证不同线程对某个变量完成操作后的结果对其它线程及时可见,即该共享变量一旦改变所有线程立即可见。

9.1.1、volatile内存语义

在这里插入图片描述

9.1.2、vlolatile如何保证可见性和有序性?

内存屏障(Memory Barrier)



9.2、内存屏障(Memory Barrier)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



9.3、读写屏障之插入策略

在这里插入图片描述

  1. 读屏障

在这里插入图片描述

在这里插入图片描述

  1. 写屏障

在这里插入图片描述

在这里插入图片描述

  1. volatile变量的读写过程:JAVA内存模型中定义的8种每个线程自己的工作内存与主物理内存之间的原子操作。

    在这里插入图片描述

    在这里插入图片描述



9.4、volatile无原子性

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



9.5、指令禁重排

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



9.6、volatile使用场景

  1. 单一赋值可以,但是含复合运算赋值不可以(i++)类
volatile int a=10;
volatile boolean flag=false;
  1. 状态标志,判断业务是否结束
import java.util.concurrent.TimeUnit;

/**
 * @Description: volatile使用:作为一个布尔状态标志,用于指示发生了一个重要的一次性事件,列如完成初始化或任务结束
 * 理由:状态标志并不依赖于程序内任何其他状态,且通常只有一种状态装换
 * 例子:判断业务是否结束
 * @Author: yangyb
 * @Date:2022/10/7 17:22
 * Version: 1.0
 **/
public class UseVolatileDemo {

    private volatile static boolean flog=true;

    public static void main(String[] args){
        new Thread(()->{
            while (flog){
                System.out.println("do something");
            }
        },"t1").start();

        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            flog=false;
        },"t2").start();
    }

}
  1. 开销较低的读、写锁策略
    /*
    * 使用:当读远多于写,结合使用内部锁和volatile变量来减少同步的开销
    * 理由:利用volatile保证读取操作的可见性,利用synchronized保证复合操作的原子性
    * */

    public class Counter{
        private volatile int value;

        public int getValue(){
            return value; // 利用volatile保证读取操作的可见性
        }

        public synchronized int increment(){
            return value++; // 利用synchronized保证复合操作的原子性
        }
    }
  1. DCL双检锁的发布

    在这里插入图片描述

    单线程:

    在这里插入图片描述

    多线程:

    在这里插入图片描述

    在这里插入图片描述

    解决方法:

    在这里插入图片描述



9.7、总结

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



10、CAS



10.1、CAS简介

10.1.1、说明

  • compare and swap的缩写,中文翻译成比较并交换,实现并发算法时场用到的一种技术。它包含三个操作数——内存位置、预期原值及更新值。
  • 执行CAS操作的时候,将内存位置的值与预期原值比较:如果相匹配,那么处理器会自动将该位置值更新为新值,如果不匹配,处理器不做任何操作,多个线程同时执行CAS操作只有一个会成功。

10.1.2、原理

CAS有三个操作数,位置内存值V,旧的预期值A,要修改的更新值B。当且仅当旧的预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做或重来。

在这里插入图片描述

10.1.2、硬件级别保证

在这里插入图片描述

源码分析

在这里插入图片描述


    /**
     * Atomically sets the value to the given updated value
     * if the current value {@code ==} the expected value.
     *
     * @param expect the expected value
     * @param update the new value
     * @return {@code true} if successful. False return indicates that
     * the actual value was not equal to the expected value.
     */
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

问题:Unsafe类是什么?



10.2、CAS底层原理-Unsafe类

10.2.1、源码分析

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

10.2.2、总结

在这里插入图片描述



10.3、CAS之原子引用 AtomicReference

import java.util.concurrent.atomic.AtomicReference;

/**
 * @Description: 原子引用
 * @Author: yangyb
 * @Date:2022/10/7 21:17
 * Version: 1.0
 **/

class User{
    private int age;
    private String name;

    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}

public class AtomicReferenceDemo {

    public static void main(String[] args){

        AtomicReference<User> userAtomicReference = new AtomicReference<>();
        User zhangSan = new User(10, "张三");
        User liSi = new User(12, "李四");

        userAtomicReference.set(zhangSan);
        System.out.println(userAtomicReference.compareAndSet(zhangSan,liSi)+"\t"+userAtomicReference.get().toString());
        System.out.println(userAtomicReference.compareAndSet(zhangSan,liSi)+"\t"+userAtomicReference.get().toString());

    }

}

在这里插入图片描述



10.4、CAS与自旋锁

10.4.1、简介

在这里插入图片描述

在这里插入图片描述

10.4.2、手写自旋锁,借鉴CAS思想

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Description: 自旋锁实现
 * 目标:实现一个自旋锁,复习CAS思想
 * 自旋锁的好处:循环比较获取没有类似wait的阻塞
 * 通过CAS操作完成自旋锁,A线程先进来调用myLock方法自己持有锁5秒钟,B线程随后进来发现当前线程持有锁,
 * 所以只能通过自旋等待,直到A释放锁后B随后抢到
 * @Author: yangyb
 * @Date:2022/10/8 20:50
 * Version: 1.0
 **/
public class SpinLockDemo {

    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void lock() {
        // 获取当前线程
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t 线程come in!");
        while (!atomicReference.compareAndSet(null, thread)) {
            System.out.println(Thread.currentThread().getName() + "\t 线程 自旋中!,等待A线程释放锁");
        }
        System.out.println(Thread.currentThread().getName() + "\t 线程 自旋结束");
    }

    public void unlock() {
        // 获取当前线程
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName() + "\t task over,unlock!");
    }

    public static void main(String[] args) {

        SpinLockDemo spinLockDemo = new SpinLockDemo();
        new Thread(() -> {
            spinLockDemo.lock();
            // 休息5秒钟
            try {
                TimeUnit.MILLISECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.unlock();
        }, "A").start();

        // 休息1秒钟,保证A线程先启动
        try {
            TimeUnit.MILLISECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            spinLockDemo.lock();
            // 休息5秒钟
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLockDemo.unlock();
        }, "B").start();

    }

}

在这里插入图片描述

在这里插入图片描述



10.5、CAS两大缺点

10.5.1、循环时间长,CPU开销大

在这里插入图片描述

10.5.2、ABA问题的产生

在这里插入图片描述



10.6、邮戳AtomicStampedReference

(1)没有用AtomicStampedReference,会发生ABA问题

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description: ABADemo
 * @Author: yangyb
 * @Date:2022/10/8 21:48
 * Version: 1.0
 **/
public class ABADemo {

    static AtomicInteger atomicInteger = new AtomicInteger(100);

    public static void main(String[] args) {

        new Thread(() -> {
            atomicInteger.compareAndSet(100, 101);
            try {
                TimeUnit.MILLISECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            atomicInteger.compareAndSet(101, 100);
        }, "A").start();


        new Thread(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(atomicInteger.compareAndSet(100, 2022) + "\t" + atomicInteger.get());
        }, "B").start();
    }
}

A线程中间悄无声息的替换了数据,B没有

在这里插入图片描述

(2)加上AtomicStampedReference,解决ABA问题

/**
 * @Description: ABADemo
 * @Author: yangyb
 * @Date:2022/10/8 21:48
 * Version: 1.0
 **/
public class ABADemo {

    static AtomicInteger atomicInteger = new AtomicInteger(100);
    static AtomicStampedReference stampedReference = new AtomicStampedReference<>(100, 1);

    public static void main(String[] args) {

        new Thread(() -> {
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t" + "首次版本号:" + stamp);
            //暂停一下,保证后面的B线程拿到的版本号和我一样
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            stampedReference.compareAndSet(100, 101, stampedReference.getStamp(), stampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t" + "2次版本号:" + stampedReference.getStamp());
            stampedReference.compareAndSet(101, 100, stampedReference.getStamp(), stampedReference.getStamp() + 1);
            System.out.println(Thread.currentThread().getName() + "\t" + "3次版本号:" + stampedReference.getStamp());
        }, "A").start();


        new Thread(() -> {
            int stamp = stampedReference.getStamp();
            System.out.println(Thread.currentThread().getName() + "\t" + "首次版本号:" + stamp);
            // 暂停一秒钟,等待上面的t3线程,发生ABA问题
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            boolean b = stampedReference.compareAndSet(100, 202, stamp, stampedReference.getStamp() + 1);
            System.out.println(b + "\t" + stampedReference.getReference() + "\t" + stampedReference.getStamp());
        }, "B").start();
    }
}

在这里插入图片描述



11、原子类

在这里插入图片描述



11.1、基本类型原子类

  1. AtomicInterger
  2. AtomicBoolean
  3. AtomicLong
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Description: TODO
 * @Author: yangyb
 * @Date:2022/10/9 22:00
 * Version: 1.0
 **/

class MyNumber {
    AtomicInteger atomicInteger = new AtomicInteger();

    public void addPlusPlus() {
        atomicInteger.getAndIncrement();
    }
}

public class AtomicIntegerDemo {

    public static final int SIZE = 50;

    public static void main(String[] args) throws InterruptedException {
        MyNumber myNumber = new MyNumber();
        CountDownLatch countDownLatch = new CountDownLatch(SIZE);
        for (int i = 1; i < SIZE; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j < 1000; j++) {
                        myNumber.addPlusPlus();
                    }
                } finally {
                    countDownLatch.countDown();
                }
            }, String.valueOf(i)).start();
        }

        countDownLatch.await();
        System.out.println(Thread.currentThread().getName() + "\t" + "result: " + myNumber.atomicInteger.get());
    }
}



11.2、数组类型原子类

  1. AtomicIntegerArray
  2. AtomicLongArray
  3. AtomicReferenceArray

    在这里插入图片描述



11.3、引用类型原子类

  1. AtomicReference

  2. AtomicStampedReference

    在这里插入图片描述

  3. AtomicMarkableReference

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



11.4、对象的属性修改原子类

  1. AtomicIntegerFieldUpdater
  2. AtomicLongFieldUpdater
  3. AtomicReferenceFieldUpdater

    在这里插入图片描述

在这里插入图片描述



11.5、原子操作增强类

在这里插入图片描述

在这里插入图片描述



12、ThreadLocal



12.1、简介

ThreadLocal提供线程局部变量。这些变量与正常的变量不同,因为每一个线程在访问ThreadLocal实列的时候(通过其get或set方法)都有自己的、独立初始化的变量副本。ThreadLocal实例通常是类中的私有静态字段,使用它的目的是希望将状态(列入,用户ID或事物ID)与线程关联起来。

在这里插入图片描述

/**
 * @Description: ThreadLocal
 * @Author: yangyb
 * @Date:2022/10/10 21:58
 * Version: 1.0
 **/
// 资源类
class House{
     int saleCount =0;
     public synchronized void saleHouse(){
         ++saleCount;
     }
     ThreadLocal<Integer> saleVolume=ThreadLocal.withInitial(()->0);
     public void saleVolumeByThreadLocal(){
         saleVolume.set(1+saleVolume.get());
     }
}
public class ThreadLocalDemo {

    public static void main(String[] args){

        House house = new House();
        for(int i=1;i<=5;i++){
            new Thread(()->{
                try {
                    int size=new Random().nextInt(5)+1;
                    for(int j=1;j<=size;j++){
                        house.saleHouse();
                        house.saleVolumeByThreadLocal();
                    }
                    System.out.println(Thread.currentThread().getName()+"\t"+"共计卖出多少套:"+house.saleVolume.get());
                }finally {
                    house.saleVolume.remove();
                }

            },String.valueOf(i)).start();
        }

        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+"\t"+"共计卖出多少套:"+house.saleCount);
    }

}

在这里插入图片描述



12.2、ThreadLocal规范要求

在这里插入图片描述

/**
 * @Description: ThreadLocal规范
 * @Author: yangyb
 * @Date:2022/10/10 22:37
 * Version: 1.0
 **/
class MyData{
    ThreadLocal<Integer> threadLocalField=ThreadLocal.withInitial(()->0);
    public void add(){
        threadLocalField.set(1+threadLocalField.get());
    }
}
public class ThreadLocalDemoTwo {

    public static void main(String[] args){

        MyData myData = new MyData();

        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        try {
            for(int i=0;i<10;i++){
                threadPool.submit(()->{
                    Integer beforeInteger = myData.threadLocalField.get();
                    myData.add();
                    Integer afterInteger = myData.threadLocalField.get();
                    System.out.println(Thread.currentThread().getName()+"\t"+"beforeInteger:"+beforeInteger+"\t"+"afterInteger:"+afterInteger);
                });
            }
        }finally {
            threadPool.shutdown();
        }


    }

}

在这里插入图片描述

使用remove()方法以后:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Description: ThreadLocal规范
 * @Author: yangyb
 * @Date:2022/10/10 22:37
 * Version: 1.0
 **/
class MyData{
    ThreadLocal<Integer> threadLocalField=ThreadLocal.withInitial(()->0);
    public void add(){
        threadLocalField.set(1+threadLocalField.get());
    }
}
public class ThreadLocalDemoTwo {

    public static void main(String[] args){

        MyData myData = new MyData();

        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        try {
            for(int i=0;i<10;i++){
                threadPool.submit(()->{
                    try {
                        Integer beforeInteger = myData.threadLocalField.get();
                        myData.add();
                        Integer afterInteger = myData.threadLocalField.get();
                        System.out.println(Thread.currentThread().getName()+"\t"+"beforeInteger:"+beforeInteger+"\t"+"afterInteger:"+afterInteger);
                    }finally {
                        myData.threadLocalField.remove();
                    }

                });
            }
        }finally {
            threadPool.shutdown();
        }


    }

}

在这里插入图片描述



12.3、总结

在这里插入图片描述



12.4、Thread、ThreadLocal、ThreadLocalMap三者的关系

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



12.5、弱引用的引出

在这里插入图片描述

在这里插入图片描述



12.6、强引用

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



12.7、软引用

在这里插入图片描述

在这里插入图片描述



12.8、弱引用

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



12.9、虚引用

在这里插入图片描述



12.10、四中引用总结

在这里插入图片描述



12.11、ThreadLocal为何用弱引用

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



12.12、总结

在这里插入图片描述

在这里插入图片描述



13、对象内存布局和对象头



13.1、简介

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



13.2、对象头

1、对象标记Mark Word

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

总结:

在这里插入图片描述

2、类元信息(又叫类型指针)

在这里插入图片描述

总结:对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是那个类的实例。

3、对象头有多大?

在这里插入图片描述

在这里插入图片描述



13.3、实例数据和对齐填充

1、实例数据

在这里插入图片描述

2、对齐填充

在这里插入图片描述



13.4、对象分代年龄

在这里插入图片描述



14、synchronized与锁升级



14.1、简介

在这里插入图片描述

synchronized锁:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



14.2、synchronized锁升级流程

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



14.3、无锁

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



14.4、偏向锁

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

理论实现:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

案例说明:

在这里插入图片描述

在这里插入图片描述

重要参数说明:

在这里插入图片描述

在这里插入图片描述

代码示例:

在这里插入图片描述

在这里插入图片描述



14.5、偏向锁撤销

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

偏向锁废弃:

在这里插入图片描述



14.6、轻量级锁

在这里插入图片描述

在这里插入图片描述

轻量级锁的获取:

在这里插入图片描述

在这里插入图片描述

补充:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



14.7、重锁

在这里插入图片描述



14.8、锁升级后和hashCode的关系

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



14.9、各种锁优缺点总结

在这里插入图片描述

在这里插入图片描述



14.10、锁消除

在这里插入图片描述



14.11、锁粗化

在这里插入图片描述



14.12、总结

在这里插入图片描述



15、AQS



15.1、简介

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



15.2、AQS作用

15.2.1、源码查看



在这里插入图片描述

在这里插入图片描述



15.3、AQS之state和LCH队列

在这里插入图片描述



15.4、AQS自身属性和Node节点

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



15.5、AQS源码分析

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

15.5.1、非公平锁

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



15.6、AQS源码总结

。。。。。。。。。。。。。。。。。。。。。。。。。。。





16、读写锁



16.1、简介

在这里插入图片描述



16.2、锁演化

在这里插入图片描述

在这里插入图片描述

16.2.1、读写锁的意义和特点

在这里插入图片描述



16.3、锁降级

在这里插入图片描述



16.4、不可锁升级

在这里插入图片描述

在这里插入图片描述

结论:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



17、stampedLock



17.1、简介

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



17.2、stampedLock锁特点

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

缺点:

在这里插入图片描述



版权声明:本文为YYBDESHIJIE原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。