Java:多线程讲解

什么是多线程?-----比较正规的说法

简单举例:某网盘可以同时下好几个文件,这就是所谓的多线程。

多线程的创建方式一:

Java:多线程讲解_第1张图片

MyThread类代码:

package com.itheima.d1_create_thread;

public class MyThread extends Thread{
    @Override
    public void run() {
        // 描述线程的执行任务。
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程MyThread输出:" + i);
        }
    }
}

开始测试代码---------

ThreadTest1类代码:

package com.itheima.d1_create_thread;

public class ThreadTest1 {
    public static void main(String[] args) {
        // 3、创建MyThread线程类的对象代表一个线程
        Thread t = new MyThread();
        // 4、启动线程(自动执行run方法的)
        t.start();  // main线程 t线程    输出顺序不定。
        for(int i = 1; i <= 5; i++) {
            System.out.println("主线程main输出:" + i);
        }
    }

}

方法一优缺点:

Java:多线程讲解_第2张图片

插曲----多线程的注意事项:

Java:多线程讲解_第3张图片

多线程的创建方式二:实现Runnable接口

Java:多线程讲解_第4张图片

MyRunnable类代码:
 

package com.itheima.d1_create_thread;
//实现Runnable接口
public class MyRunnable implements Runnable{
    //1、定义一个任务类,实现Runnable接口
    //快捷键重写方法:鼠标放在implements开头alt+enter
    @Override
    public void run() {
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程输出" + i);
        }
    }
}

开始测试代码---------

ThreadTest2类代码:

package com.itheima.d1_create_thread;

public class ThreadTest2 {
    public static void main(String[] args) {
        // 3、创建任务对象。
      Runnable target = new MyRunnable();
        // 4、把任务对象交给一个线程对象处理。
      new Thread(target).start();
        for (int i = 1; i <= 5; i++) {
            System.out.println("主线程输出" + i);
        }
    }
}

方式二的优缺点:

线程创建方式二的匿名内部类写法:

Java:多线程讲解_第5张图片

实现代码以及两种简化形式(简化写法的讲解在注释中):

package com.itheima.d1_create_thread;

public class ThreadTest2_2 {
    public static void main(String[] args) {
        // 1、直接创建Runnable接口的匿名内部类形式(任务对象)
        Runnable target = new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程1输出:" + i);
                }
            }
        };
        new Thread(target).start();
        // 简化形式1:  这就是简单的替换。
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 5; i++) {
                    System.out.println("子线程2输出:" + i);
                }
            }
        }).start();
        // 简化形式2:   Runnable接口是@FunctionalInterface,也就是函数式接口(按住ctrl点Runnable查看源码,就能看到它是函数式接口了)  可以用lambda表达式简化
        new Thread(() -> {
            for (int i = 1; i <= 5; i++) {
                System.out.println("子线程3输出:" + i);
            }
        }).start();

        //输出结果:子线程3输出:1
        //子线程3输出:2
        //子线程1输出:1
        //子线程3输出:3
        //子线程2输出:1
        //子线程3输出:4
        //子线程1输出:2
        //子线程3输出:5
        //子线程2输出:2
        //子线程1输出:3
        //子线程1输出:4
        //子线程2输出:3
        //子线程2输出:4
        //子线程1输出:5
        //子线程2输出:5
        //
        //Process finished with exit code 0
        //正常输出。





    }
}

注意:前两种线程创建方式都存在的一个问题:

假如线程执行完毕后有一些数据需要返回,他们重写的run方法均不能直接返回结果。

那如何解决?

JDK 5.0提供了Callable接口和FutureTask类来实现(多线程的第三种创建方式)

这种方式最大的优点:可以返回线程执行完毕后的结果。

多线程的第三种创建方式:利用Callable接口、FutureTask类来实现。

Java:多线程讲解_第6张图片

FutureTask的API:

Java:多线程讲解_第7张图片

MyCallable类的代码:

package com.itheima.d1_create_thread;

import java.util.concurrent.Callable;

public class MyCallable implements Callable {
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }
    @Override
    public String call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return "线程求出了1-" + n + "的和是:" + sum;
    }

}

开始测试代码---------

ThreadTest3类的代码:

package com.itheima.d1_create_thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class ThreadTest3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 3、创建一个Callable的对象
        Callable call = new MyCallable(100);
        // 4、把Callable的对象封装成一个FutureTask对象(任务对象)
        // 未来任务对象的作用?
        // 1、是一个任务对象,实现了Runnable对象.
        // 2、可以在线程执行完毕之后,用未来任务对象调用get方法获取线程执行完毕后的结果。
        FutureTask f1  = new FutureTask<>(call);
        // 5、把任务对象交给一个Thread对象
        new Thread(f1).start();


        Callable call2 = new MyCallable(200);
        FutureTask f2  = new FutureTask<>(call2);
        new Thread(f2).start();


        // 6、获取线程执行完毕后返回的结果。
        // 注意:如果执行到这儿,假如上面的线程还没有执行完毕
        // 这里的代码会暂停,等待上面线程执行完毕后才会获取结果。
        String rs = f1.get();
        System.out.println(rs);

        String rs2 = f2.get();
        System.out.println(rs2);

        //线程求出了1-100的和是:5050
        //线程求出了1-200的和是:20100
    }
}

线程创建方式三的优缺点:

Thread类提供的常用方法和构造器:

Java:多线程讲解_第8张图片

对Thread类提供的常用方法和构造器进行代码演示:

先创建一个MyThread类:

package com.itheima.d2_thread_api;

public class MyThread extends Thread {
    public MyThread(String name){
        super(name); // 为当前线程设置名字了
    }
    @Override
    public void run() {
        // 哪个线程执行它,它就会得到哪个线程对象。
        Thread t = Thread.currentThread();
        for (int i = 1; i <= 3; i++) {
            System.out.println(t.getName() + "输出:" + i);
        }
    }
}

开始测试代码---------

ThreadTest1类代码:

package com.itheima.d2_thread_api;

public class ThreadTest1 {
    public static void main(String[] args) {
        Thread t1 = new MyThread("1号线程");
        // t1.setName("1号线程");
        t1.start();
        System.out.println(t1.getName()); // 这里是没起名的结果: Thread-0  格式:线程-索引
                                            //起名了就是1号线程

        Thread t2 = new MyThread("2号线程");
        // t2.setName("2号线程");
        t2.start();
        System.out.println(t2.getName()); // Thread-1

        // 主线程对象的名字
        // 哪个线程执行它,它就会得到哪个线程对象。
        Thread m = Thread.currentThread();
        m.setName("最牛的线程");
        System.out.println(m.getName()); // 没起名的结果是:main

        for (int i = 1; i <= 5; i++) {
            System.out.println(m.getName() + "线程输出:" + i);
        }
        //1号线程
        //2号线程
        //最牛的线程
        //最牛的线程线程输出:1
        //1号线程输出:1
        //1号线程输出:2
        //2号线程输出:1
        //1号线程输出:3
        //最牛的线程线程输出:2
        //2号线程输出:2
        //最牛的线程线程输出:3
        //2号线程输出:3
        //最牛的线程线程输出:4
        //最牛的线程线程输出:5
        //
        //Process finished with exit code 0
    }
}

开始测试代码---------

ThreadTest2类代码:

package com.itheima.d2_thread_api;
/**
 * 目标:掌握sleep方法,join方法的作用。
 */

public class ThreadTest2 {
    public static void main(String[] args) throws Exception {
        for (int i = 1; i <= 5; i++) {
            System.out.println(i);
            // 休眠5s
            if(i == 3){
                // 会让当前执行的线程暂停5秒,再继续执行
                // 项目经理让我加上这行代码,如果用户交钱了,我就注释掉!
                Thread.sleep(5000);
            }
        }

        // join方法作用:让当前调用这个方法的线程先执行完。
        Thread t1 = new MyThread("1号线程");
        t1.start();
        t1.join();

        Thread t2 = new MyThread("2号线程");
        t2.start();
        t2.join();

        Thread t3 = new MyThread("3号线程");
        t3.start();
        t3.join();
        //1
        //2
        //3
        //4
        //5
        //1号线程输出:1
        //1号线程输出:2
        //1号线程输出:3
        //2号线程输出:1
        //2号线程输出:2
        //2号线程输出:3
        //3号线程输出:1
        //3号线程输出:2
        //3号线程输出:3
    }
}

什么是线程安全问题?

多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题。

用一个取钱案例讲解线程安全以及如何解决:

Java:多线程讲解_第9张图片

创建账户类:

package com.itheima.d3_thread_safe;

public class Account {
    private String cardId; // 卡号
    private double money; // 余额。

    public Account() {
    }
    // 小明 小红同时过来的
    public void drawMoney(double money) {
        // 先搞清楚是谁来取钱?
        String name = Thread.currentThread().getName();
        // 1、判断余额是否足够
        if(this.money >= money){
            System.out.println(name + "来取钱" + money + "成功!");
            this.money -= money;
            System.out.println(name + "来取钱后,余额剩余:" + this.money);
        }else {
            System.out.println(name + "来取钱:余额不足~");
        }
    }
    public Account(String cardId, double money) {
        this.cardId = cardId;
        this.money = money;
    }

    public String getCardId() {
        return cardId;
    }

    public void setCardId(String cardId) {
        this.cardId = cardId;
    }

    public double getMoney() {
        return money;
    }

    public void setMoney(double money) {
        this.money = money;
    }
}

创建线程类:

package com.itheima.d3_thread_safe;

public class DrawThread extends Thread {
    private Account acc;
    public DrawThread(Account acc, String name){
        super(name);
        this.acc = acc;
    }
    @Override
    public void run() {
        // 取钱(小明,小红)
        acc.drawMoney(100000);
    }
}

测试类:

package com.itheima.d3_thread_safe;
/**
 * 目标:模拟线程安全问题。
 */
public class ThreadTest {
    public static void main(String[] args) {
        // 1、创建一个账户对象,代表两个人的共享账户。
        Account acc = new Account("ICBC-110", 100000);
        // 2、创建两个线程,分别代表小明 小红,再去同一个账户对象中取钱10万。
        new DrawThread(acc, "小明").start(); // 小明
        new DrawThread(acc, "小红").start(); // 小红
        //小红来取钱100000.0成功!
        //小明来取钱100000.0成功!
        //小明来取钱后,余额剩余:-100000.0
        //小红来取钱后,余额剩余:0.0
        //出现问题。
    }
}

测试类注释中已经说明出现了线程安全的问题,该如何解决:

提出线程同步概念:


解决线程安全问题的方案。


线程同步的思想:


让多个线程实现先后依次访问共享资源,这样就解决了安全问题。

线程同步的常见方案:

同步代码块:

Java:多线程讲解_第10张图片

对上述取钱案例进行修改:

只需要对部分代码更改即可:

 public void drawMoney(double money) {
        // 先搞清楚是谁来取钱?
        String name = Thread.currentThread().getName();
        // 1、判断余额是否足够
        //实例方法推荐使用this当锁
        synchronized (this) {
            if(this.money >= money){
                System.out.println(name + "来取钱" + money + "成功!");
                this.money -= money;
                System.out.println(name + "来取钱后,余额剩余:" + this.money);
            }else {
                System.out.println(name + "来取钱:余额不足~");
            }
        }
    }

这里注意区分实例方法内推荐使用this当作锁,静态方法内推荐使用类名.class当作锁:

这里把静态方法内使用类名.class的例子也写了出来:

public static void test(){
        synchronized (Account.class){

        }
    }

同步方法:

Java:多线程讲解_第11张图片

对上述取钱案例进行修改:

只需要对部分代码更改即可:

public synchronized void drawMoney(double money) {
        // 先搞清楚是谁来取钱?
        String name = Thread.currentThread().getName();
        // 1、判断余额是否足够
        if(this.money >= money){
            System.out.println(name + "来取钱" + money + "成功!");
            this.money -= money;
            System.out.println(name + "来取钱后,余额剩余:" + this.money);
        }else {
            System.out.println(name + "来取钱:余额不足~");
        }
    }

同步方法的底层原理:

Java:多线程讲解_第12张图片

Lock锁:

Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。
Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。

Lock类的构造器:

常用方法:

Java:多线程讲解_第13张图片

代码:

对上述取钱案例进行修改:

只需要对部分代码更改即可:

在Account类中修改成如下即可:

// 创建了一个锁对象
    private final Lock lk = new ReentrantLock();

  

    // 小明 小红线程同时过来的
    public void drawMoney(double money) {
        // 先搞清楚是谁来取钱?
        String name = Thread.currentThread().getName();
        try {
            lk.lock(); // 加锁
            // 1、判断余额是否足够
            if(this.money >= money){
                System.out.println(name + "来取钱" + money + "成功!");
                this.money -= money;
                System.out.println(name + "来取钱后,余额剩余:" + this.money);
            }else {
                System.out.println(name + "来取钱:余额不足~");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lk.unlock(); // 解锁
        }
    }

线程通信(了解即可):

当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,并避免无效的资源争夺。

线程通信的常见模型:

Java:多线程讲解_第14张图片

Object类的等待和唤醒方法:

Java:多线程讲解_第15张图片

注意:

通过案例讲解:

 需求:3个生产者线程,负责生产包子,每个线程每次只能生产1个包子放在桌子上
            2个消费者线程负责吃包子,每人每次只能从桌子上拿1个包子吃。

定义Desk类:

package com.itheima.d7_thread_communication;

import java.util.ArrayList;
import java.util.List;

public class Desk {
    private List list = new ArrayList<>();

    // 放1个包子的方法
    // 厨师1 厨师2 厨师3
    public synchronized void put() {
        try {
            String name = Thread.currentThread().getName();
            // 判断是否有包子。
            if(list.size() == 0){
                list.add(name + "做的肉包子");
                System.out.println(name + "做了一个肉包子~~");
                Thread.sleep(2000);

                // 唤醒别人, 等待自己
                this.notifyAll();
                this.wait();
            }else {
                // 有包子了,不做了。
                // 唤醒别人, 等待自己
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // 吃货1 吃货2
    public synchronized void get() {
        try {
            String name = Thread.currentThread().getName();
            if(list.size() == 1){
                // 有包子,吃了
                System.out.println(name  + "吃了:" + list.get(0));
                list.clear();
                Thread.sleep(1000);
                this.notifyAll();
                this.wait();
            }else {
                // 没有包子
                this.notifyAll();
                this.wait();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

测试类ThreadTest:

package com.itheima.d7_thread_communication;

/**
 * 目标:了解一下线程通信。
 */
public class ThreadTest {
    public static void main(String[] args) {
        //   需求:3个生产者线程,负责生产包子,每个线程每次只能生产1个包子放在桌子上
        //      2个消费者线程负责吃包子,每人每次只能从桌子上拿1个包子吃。
        Desk desk  = new Desk();

        // 创建3个生产者线程(3个厨师)
        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师1").start();

        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师2").start();

        new Thread(() -> {
            while (true) {
                desk.put();
            }
        }, "厨师3").start();

        // 创建2个消费者线程(2个吃货)
        new Thread(() -> {
            while (true) {
                desk.get();
            }
        }, "吃货1").start();

        new Thread(() -> {
            while (true) {
                desk.get();
            }
        }, "吃货2").start();
    }
}

线程池:

线程池就是一个可以复用线程的技术。

不使用线程池会有什么问题?

谁代表线程池:

如何得到线程池对象:

Java:多线程讲解_第16张图片

方法一:

Java:多线程讲解_第17张图片

Java:多线程讲解_第18张图片

ExecutorService的常用方法:

Java:多线程讲解_第19张图片

附带代码讲解:

先创建MyRunnable类:

package com.itheima.d8_thread_pool;

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        // 任务是干啥的?
        System.out.println(Thread.currentThread().getName() + " ==> 输出666~~");
        try {
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

测试类ThreadPoolTest1:

package com.itheima.d8_thread_pool;

import java.util.concurrent.*;
/**
 * 目标:掌握线程池的创建。
 */
public class ThreadPoolTest1 {
    public static void main(String[] args) {
        // 1、通过ThreadPoolExecutor创建一个线程池对象。
        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());

        Runnable target = new MyRunnable();
        pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
        pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
        pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
        pool.execute(target); 
        pool.execute(target);
        pool.execute(target);
        pool.execute(target); 
        // 到了临时线程的创建时机了
        pool.execute(target);
        pool.execute(target);
        // 到了新任务的拒绝时机了!
        pool.execute(target);

        // pool.shutdown(); // 等着线程池的任务全部执行完毕后,再关闭线程池
        // pool.shutdownNow(); // 立即关闭线程池!不管任务是否执行完毕!
    }
}

上述还剩此方法没讲解:

代码:

先实现MyCallable类:

package com.itheima.d8_thread_pool;

import java.util.concurrent.Callable;

/**
 * 1、让这个类实现Callable接口
 */
public class MyCallable implements Callable {
    private int n;
    public MyCallable(int n) {
        this.n = n;
    }

    // 2、重写call方法
    @Override
    public String call() throws Exception {
        // 描述线程的任务,返回线程执行返回后的结果。
        // 需求:求1-n的和返回。
        int sum = 0;
        for (int i = 1; i <= n; i++) {
            sum += i;
        }
        return Thread.currentThread().getName() + "求出了1-" + n + "的和是:" + sum;
    }
}

测试类ThreadPoolTest2:

package com.itheima.d8_thread_pool;

import java.util.concurrent.*;

/**
 * 目标:掌握线程池的创建。
 */
public class ThreadPoolTest2 {
    public static void main(String[] args) throws Exception {
        // 1、通过ThreadPoolExecutor创建一个线程池对象。
        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());

        // 2、使用线程处理Callable任务。
        Future f1 = pool.submit(new MyCallable(100));
        Future f2 = pool.submit(new MyCallable(200));
        Future f3 = pool.submit(new MyCallable(300));
        Future f4 = pool.submit(new MyCallable(400));

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}

线程池注意事项:

Java:多线程讲解_第20张图片

新任务拒绝策略:

Java:多线程讲解_第21张图片

Executors:

Java:多线程讲解_第22张图片

注意:

代码示例:

package com.itheima.d8_thread_pool;

import java.util.concurrent.*;

/**
 * 目标:掌握线程池的创建。
 */
public class ThreadPoolTest3 {
    public static void main(String[] args) throws Exception {
        // 1、通过ThreadPoolExecutor创建一个线程池对象。
//        ExecutorService pool = new ThreadPoolExecutor(3, 5, 8,
//                TimeUnit.SECONDS, new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
//                new ThreadPoolExecutor.CallerRunsPolicy());

        // 1-2 通过Executors创建一个线程池对象。
        ExecutorService pool = Executors.newFixedThreadPool(17);
        // 核心线程数量到底配置多少呢???
        // 计算密集型的任务:核心线程数量 = CPU的核数 + 1
        // IO密集型的任务:核心线程数量 = CPU核数 * 2

        // 2、使用线程处理Callable任务。
        Future f1 = pool.submit(new MyCallable(100));
        Future f2 = pool.submit(new MyCallable(200));
        Future f3 = pool.submit(new MyCallable(300));
        Future f4 = pool.submit(new MyCallable(400));

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        System.out.println(f4.get());
    }
}

代码里讲到了核心线程数量的配置,怎么查看CPU的核数:

ctrl+alt+delete,打开任务管理器,选择性能一栏,可以看到:

Java:多线程讲解_第23张图片

在这个界面下找到逻辑处理器,就是你的CPU核数,按照代码中的公式套进去就行了。

Executors使用可能存在的陷阱:

Java:多线程讲解_第24张图片

进程:

Java:多线程讲解_第25张图片

并发的含义:

并行的理解:

比如你的CPU核数如果是16的话,那么同一时刻,可以同时有16个线程被执行。

线程的生命周期:

Java线程的状态:

Java:多线程讲解_第26张图片

Java:多线程讲解_第27张图片

状态说明:

Java:多线程讲解_第28张图片

乐观锁和悲观锁解释:

悲观锁:一上来就加锁,没有安全感。每次只能一个线程进入访问完毕后,再解锁。线程安全,性能较差!
乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题的时候才开始控制。线程安全,性能较好。

悲观锁:

目前的需求:1个变量,100个线程,每个线程对其加100次-----正常结果就是10000。

准备一个MyRunnable类:

package com.itheima.d9_tz;

public class MyRunnable implements Runnable {
    private int count;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
           
            System.out.println("count==========>"+(++count));
            
        }
    }
}

测试类Test1:

package com.itheima.d9_tz;

public class Test1 {
    public static void main(String[] args) {
        //悲观锁:一上来就加锁,没有安全感。每次只能一个线程进入访问完毕后,再解锁。线程安全,性能较差!
        //乐观锁:一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问题的时候才开始控制。线程安全,性能较好。
        //需求:1变量,100个线程,每个线程对其加100次
        Runnable target = new MyRunnable2();

        for (int i = 1; i <=100; i++) {
            new Thread(target).start();//没到10000,出现线程安全。这个数不一定,也有可能会到10000
            //但是它终归是会出现线程安全的。
        }
    }
}

出现线程安全,解决代码:

 //悲观锁
            synchronized (this) {
                System.out.println("count==========>"+(++count));
            }

乐观锁:

需求同上。测试类中把这句Runnable target = new MyRunnable();改成Runnable target = new MyRunnable2();

准备一个MyRunnable2类,与MyRunnable类一样,如果要用乐观锁的话,MyRunnable2的类该怎么修改:

package com.itheima.d9_tz;

import java.util.concurrent.atomic.AtomicInteger;

public class MyRunnable2 implements Runnable {
    //private int count;
    //整数修改的乐观锁  原子类实现的
    private AtomicInteger count = new AtomicInteger();
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("count==========>"+(count.incrementAndGet()));
            //用到了CAS算法
            
        }
    }
}

注释中写了用到CAS算法,为什么称为CAS算法:-----下图即为答案

Java:多线程讲解_第29张图片

weakCompareAndSetInt方法   --------CAS

你可能感兴趣的:(java,java,开发语言,tomcat,intellij-idea,maven,spring,cloud,spring)