第三十讲——多线程

第三十讲——多线程


1——多线程 进程 程序

多线程(Thread)

当一个程序需要多个事件同时进行(或交替运行)时就需要用到多线程;

解决了,在说话的同时能够听的问题。


进程(Process)

在操作系统中运行的程序就是进程,比如正在运行的;QQ、打游戏、爱奇艺、、、

一个进程里可以有多个线程,如视频中、能同时听声音、看图像、看弹幕等待、、

没跑起来叫程序,跑起来就叫进程

  • 程序

    程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

  • 进程

    进程是程序执行的过程,是一个动态的概念。

    进程中至少有一个线程

PS: 许多多线程是模拟出来的,真正的多线程是指多个CPU,即多核,如服务器。

​ 模拟的多线程,即在一个CPU下,在同一个时间点,CPU只能执行一个代码,因为切换的很快,所以就有同时执行的错觉。 就像边吃饭边开手机,其实在一瞬间,你只做了一件事。

计算机处理一道(如两数相加)指令需要约2~4纳秒;ns(nanosecond):纳秒,一秒的十亿分之一


核心概念

  • 线程是独立的执行路径。
  • 程序运行时,即使没有创建线程,后台也会有多线程,如;主线程、gc线程(守护线程)。
  • main() 即主线程(用户进程),为系统的入口,用于执行整个程序。
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统密切相关的,先后顺序不是人为干预的。
  • 在对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。
  • 线程会带来额外的开销,如CPU调度的时间,并发控制开销。
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致。

2——Thread

共三种方式创建多线程,还有一种 Callable 接口,目前不用

方法一 继承Thread(重点)

Application

package Thread;

// 创建多线程的方式之一; 继承Thread类,重写 run()方法,调用 start 开启线程
public class Thread01 extends Thread{
    @Override
    public void run() {
        // run 方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("晴天");
        }
    }
    // main线程  主线程
    public static void main(String[] args) {

        // 创建一个线程对象
        Thread01 thread01 = new Thread01();

        // start 开启线程
        thread01.start();

        for (int i = 0; i < 2000; i++) {
            System.out.println("稻香");
        }
        // 在稻香里穿插了晴天,说明他们是一起交替运行的,不是线性执行
        // 线程开启不一定立即执行,由 CPU 调度。谁先谁后。
    }
}

网图下载

利用多线程同时下载多个网图

准备工作

需要用到: conmonsio——下载工具类库

conmonsio 2.11.0版        百度云下载


下载压缩包后 将 压缩包中的jar 文件复制到 创建的 Lib 包下;

然后

点击 add as Library ( 创建库) 点击确认

然后在 左上角 File 中点击 Project structure 中的Libraries 就能看到 Lib 的目录 说明添加成功


Application

package Thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

// 练习 Thread , 实现多线程同步下载图片
public class Thread02 extends Thread {

    private String URL; // 网络图片的地址
    private String name; // 保存的文件名

    // 只要写 类名的首字 是快捷方法 就会弹出整个类名
    // 因为是私有的属性 所以不能用快捷键出构造方法
    public Thread02(String url,String name){
        this.URL = url;
        this.name = name;
    }
    // 下载图片线程的执行体
    @Override
    public void run() {
        webDownLoader webDownLoader = new webDownLoader();
        webDownLoader.downLoader(URL,name);
        System.out.println("下载了 全路径名为: "+name);
    }

    public static void main(String[] args) {
        Thread02 t1 = new Thread02("https://pic.cnblogs.com/avatar/2102408/20211029202135.png","E:\\File/1.jpg");
        Thread02 t2 = new Thread02("https://pic.cnblogs.com/avatar/2102408/20211029202135.png","E:\\File/2.jpg");
        Thread02 t3 = new Thread02("https://pic.cnblogs.com/avatar/2102408/20211029202135.png","E:\\File/3.jpg");

        // 线程并不是 按照顺序 执行根据 CPU 进行调度 人为不能干涉 所以下载的顺序可能每次都不一样
        t1.start();
        t2.start();
        t3.start();


    }

    // 下载器
    class webDownLoader{
        // 下载方法
        public void downLoader (String url , String name){
            try {
                FileUtils.copyURLToFile(new URL(url),new File(name));
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("IO 异常,downLoader 方法出现问题");
            }

        }
    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.Thread02
下载了 文件名为: E:\File/3.jpg
下载了 文件名为: E:\File/2.jpg
下载了 文件名为: E:\File/1.jpg

Process finished with exit code 0

get 新技能 阿帕奇 公司下 conmmons 工具类库 --> FileUtils --> copyURLToFile()方法

FileUtils.copyURLToFile(new URL(URL),new File(fileName));

方法二 实现Runnable 接口(重点)

推荐使用 Runnable 方便灵活 ,方便同一个对象被多个线程使用

Application

package Thread;

/**
 * 创建多线程方法 2 ;
 * 1. 实现runnable 接口
 * 2. 重写 run 方法
 * 3. 实例化该实现类,将该类丢入 new Thread() 中 调用 start 方法
 */
// 1. 实现runnable 接口
public class Thread03 implements Runnable {

    // 2. 重写 run 方法
    // run 方法线程体
    @Override
    public void run() {

        for (int i = 0; i < 20; i++) {
            System.out.println("0 "+i);
        }

    }

    public static void main(String[] args) {

       //  3. 实例化该实现类,丢入 new Thread() 中 调用 start 方法
        Thread03 thread03 = new Thread03();

        // 创建线程对象, 通过线程对象来开启我们的线程,代理
        new Thread(thread03).start();

        for (int i = 0 ;i<20;i++){
            System.out.println("A "+i);
        }
    }
    /**
     * Thread 实现了 Runnable 接口
     */
}

特点 ;

  • 继承 Thread 类
    • 子类 继承 Thread 具备多线程能力
    • 启动线程 : 子类对象.start
    • 不建议使用,避免 OOP 单继承局限性
  • 实现 Runnable 接口
    • 实现 RUnnable 具有多线程能力
    • 启动线程: 实例化实现类,丢入 Thread 对象. start()
    • 推荐使用 : 避免单继承局限性, 灵活方便,方便同一个对象被多个线程使用

Runnable 练习(龟兔赛跑)

  1. 首先有个赛道距离,离终点越来越近
  2. 判断比赛是否结束
  3. 打印胜出者
  4. 龟兔赛跑开始
  5. 模拟兔子睡觉
  6. 乌龟获得胜利

Application

package Thread;


// 模拟龟兔赛跑
public class Race  implements Runnable{

    // 胜利者
    private static  String winner;
    /**
    - 知道这里为什么是 static 了
    - 这样设置 保证 多个线程 是只有一个胜者 , 配合 判定游戏是否结束的方法  当胜者不为 null 其他线程就都不能跑了
    - 静态变量 保证只有一个胜者  如果不设置 static 的话 每个线程 都是跑自己的  会有多个胜者 */

    @Override
    public void run() {

        for (int i = 0; i <= 100; i++) {
            // currentThread() 返回对当前正在执行的线程对象的引用。再getName 模拟的多线程 当前的线程对象只有一个
            
            if (Thread.currentThread().getName().equals("兔子")&i%10==0){
                
                // 相对于 轮到兔子线程的时候就睡 5 毫秒, 就给乌龟跑
                try {
                    
                    // 这个是指兔子的线程
                    Thread.sleep(3);

                } catch (InterruptedException e) {

                    e.printStackTrace();

                }
            }



            // 判断比赛是否结束
            boolean flag = gameOver(i);
            
            //  currentThread() 返回对当前正在执行的线程对象的引用。再getName 模拟的多线程 当前的线程对象只有一个

            // 比赛结束  退出程序
            if (flag){
                break;
            }
                System.out.println(Thread.currentThread().getName()+"-->跑了"+i+"步");

        }



    }
    // 判断比赛是否完成
    private boolean gameOver(int steps){
      
        // 判断比赛是否有胜利者
        if (winner!=null){ // 已经存在胜利者了
               // 为了 多个线程跑的时候 仅有一个胜者 这里要判定 胜者有没有出现 一出现 , 所有线程 都抛出 break 去掉这段会有多个胜利者, 判断条件最好是  静态的变量
            return true;
            
        }else { // 如果没有 当steps 为 100 时 看是谁在跑 然后赋值 最后打印退出
            if (steps>=100){
                winner = Thread.currentThread().getName();
                System.out.println("winner is "+winner);
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        
        Race r1 = new Race();
        Thread t1 = new Thread(r1,"乌龟");
        Thread t2 = new Thread(r1,"兔子");

        t1.start();
        t2.start();
    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.Race

    略...
winner is 乌龟

Process finished with exit code 0


方法三 实现Callable接口( 了解即可 )

  1. 实现 Callable 接口,需要返回值类型
  2. 重写 call 方法,需要抛出异常
  3. 创建目标对象
  4. 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1);
  5. 提交执行: Future result1 = ser.submit(t1);
  6. 获取结果: boolean r1 = result1.get()
  7. 关闭服务: ser.shutdownNow();

Application


package Thread;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

public class TestCallable implements Callable<Boolean> {


    private String URL; // 网络图片的地址
    private String name; // 保存的文件名

    // 只要写 类名的首字 是快捷方法 就会弹出整个类名
    public TestCallable(String url, String name) {
        this.URL = url;
        this.name = name;
    }

    // 下载图片线程的执行体
    @Override
    public Boolean call() {
        webDownLoader webDownLoader = new webDownLoader();
        webDownLoader.downLoader(URL, name);
        System.out.println("下载了 文件名为: " + name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1 = new TestCallable("https://pic.cnblogs.com/avatar/2102408/20211029202135.png", "E:\\File/1.jpg");
        TestCallable t2 = new TestCallable("https://pic.cnblogs.com/avatar/2102408/20211029202135.png", "E:\\File/2.jpg");
        TestCallable t3 = new TestCallable("https://pic.cnblogs.com/avatar/2102408/20211029202135.png", "E:\\File/3.jpg");

        // 线程并不是 按照顺序 执行根据 CPU 进行调度 人为不能干涉 所以下载的顺序可能每次都不一样

        // 1. 创建执行服务:
        ExecutorService ser = Executors.newFixedThreadPool(3);
        // 2. 提交执行:
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);


        // 3. 获取结果返回值:
        boolean rs1 = r1.get();
        boolean rs2 = r1.get();
        boolean rs3 = r1.get();
        System.out.println("----------------");
        System.out.println(rs1);
        System.out.println(rs2);
        System.out.println(rs3);
        // 4. 关闭服务:
        ser.shutdownNow();


    }

    // 下载器
    class webDownLoader {
        // 下载方法
        public void downLoader(String url, String name) {
            try {
                FileUtils.copyURLToFile(new URL(url), new File(name));
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("IO 异常,downLoader 方法出现问题");
            }

        }
    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestCallable
下载了 文件名为: E:\File/3.jpg
下载了 文件名为: E:\File/1.jpg
下载了 文件名为: E:\File/2.jpg
----------------
true
true
true

Process finished with exit code 0


3——并发问题

抢火车票

Application

package Thread;

// 多个线程 同时操作同一个对象
// 买火车票的例子

// 发现问题 : 多个线程操作同一个资源的情况下,线程不安全,数据紊乱 这是一个并发问题
public class Thread04 implements Runnable{

    // 票数
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true){
            if (ticketNums<=0){
                break;
            }
            // 模拟延时  增加一点延时 因为 CPU 执行太快了 一下子就把 票给了小明 , 所以给它延时
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            //  currentThread() 返回对当前正在执行的线程对象的引用。再getName 模拟的多线程 当前的线程对象只有一个
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"票");

        }
    }

    public static void main(String[] args) {

        Thread04 ticket = new Thread04();
        //  Thread 的构造方法  前面是调用对象 ticket 后面是线程名字
        new Thread(ticket,"啊珍").start();
        new Thread(ticket,"啊强").start();
        new Thread(ticket,"黄牛").start();
        
        // 推荐使用 :  避免单继承局限性, 灵活方便,方便同一个对象被多个线程使用 
    }

}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.Thread04
黄牛-->拿到了第8票
啊强-->拿到了第9票
啊珍-->拿到了第10票
黄牛-->拿到了第7票
啊强-->拿到了第6票
啊珍-->拿到了第5票
啊珍-->拿到了第4票
啊强-->拿到了第3票
黄牛-->拿到了第2票
黄牛-->拿到了第1票
啊强-->拿到了第0票
啊珍-->拿到了第-1票

Process finished with exit code 0


4——静态代理模式(StaticProxy)

Application

package Thread;

// 静态代理模式

/**
 * 真实对象和代理对象都要实现同一个接口
 * 代理对象要代理真实角色
 *
 * 优点:
 *       代理对象可以做真实对象做不了的事情
 *       真实对象可以专注的做自己的事
 */

public class StaticProxy {
    public static void main(String[] args) {
        /**
         * Thread( new Runnable() ).start();
         */

       new WeddingCompany(new You()).HappyMarry();
       new You().HappyMarry();
    }

}

interface Marry{
    void HappyMarry();
}

// 真实角色
class You implements Marry{
    @Override
    public void HappyMarry() {

        System.out.println("今天我结婚啦!!!");

    }

}
// 代理角色  帮助你结婚
class  WeddingCompany implements Marry{
    private Marry target;

    public WeddingCompany(Marry target) {

        this.target = target;

    }

    @Override
    public void HappyMarry() {
        before();
        // 利用代理 把 真实角色"我" 丢进去 然后调用方法
        target.HappyMarry(); // 真实角色结婚
        after();
    }
    private void before(){
        System.out.println("结婚之前,布置场地");
    }
    private void after(){
        System.out.println("结婚之后,付清尾款");
    }

}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.StaticProxy
结婚之前,布置场地
今天我结婚啦!!!
结婚之后,付清尾款
今天我结婚啦!!!

Process finished with exit code 0


5——Lamda 表达式

其实实质属于函数式编程的概念

优点;

  • 避免内部类定义过多
  • 可以让代码看起来很简洁
  • 去掉一堆没有意义的代码,只留下核心逻辑
new Thread (()-->System.out.ptintln("多线程学习 Lambda 表达式...."))

  • 理解 Functional Interface (函数式接口)是学习 Java8 Lambda 表达式的关键所在

  • 函数式接口的定义

    • 任何接口,如果只包含唯一一个抽象方法,那么他就是一个函数式接口

    • public interface Runnable {
          public abstract void run();
      }
      
    • 对于函数式接口,我们可以通过 Lambda 表达式来创建该接口的对象。


Application

package Thread;

/**
 * 推导 Lambda 表达式
 */
public class TestLambda01 {
    // 3. 静态内部类
    static class Like02 implements ILike{
        @Override
        public void lambda() {
            System.out.println("i Like Lambda02");
        }
    }

    public static void main(String[] args) {
        // 4. 局部内部类
         class Like03 implements ILike{
            @Override
            public void lambda() {
                System.out.println("i Like Lambda03");
            }
        }

        ILike like01 = new Like01(); // 第一种方法  接口 new 实现类 调用lambda
        like01.lambda();

        Like02 like02 = new Like02();// 第二种方法  接口 new 静态内部实现类 调用lambda
        like02.lambda();

        Like03 like03 = new Like03();// 第三种方法  接口 new 局部内部实现类 调用lambda
        like03.lambda();
        // 5. 匿名内部类,没有类的名称 必须借助接口或者父类
         ILike like04 = new ILike(){ // 第四种方法  接口 new 匿名实现类 调用lambda
             @Override
             public void lambda() {
                 System.out.println("i Like Lambda04");
             }
         };
         like04.lambda();
         // 6.  Lambda 表达式简化 // 第五种方法  lambda 表达式
        ILike like05 = ()->{
                System.out.println("i Like Lambda05");
        };

        like05.lambda();

    }
}
// 1.  定义一个函数式接口
interface ILike{
    public abstract  void lambda(); // 默认是抽象方法
}


 // 2. 实现类
class Like01 implements ILike{
    @Override
    public void lambda() {
        System.out.println("i Like Lambda01");
    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestLambda01
i Like Lambda01
i Like Lambda02
i Like Lambda03
i Like Lambda04
i Like Lambda05

Process finished with exit code 0


Lambda

Application

package Thread;

public class TestLambda02 {

    public static void main(String[] args) {
        Love love = null;

        love = (int a)->{
            System.out.println("I Love You -> "+a);
        };
        love.love(520);

     /*   Lambda 表达式还能简化, 去中小括号,去 参数类型

     love = a-> System.out.println("I Love You -> "+a);
        love.love(50);*/
     /**
      * 总结 ;
      *   Lambda 表达式只能有一行代码的情况下才能简化成为一行,如果有多行,那么就用代码块包裹(中括号)
      *   前提-接口是函数式接口
      *   多个参数也可以去掉参数类型
      */


    }
}
interface Love {
    void love (int a);
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestLambda02
I Love You -> 520
I Love You -> 50

Process finished with exit code 0


6—— 线程方法

线程五大状态


方法 说明
setPriority(int newPriority) 更改线程的优先级
void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响
void join() 等待该线程终止。
void yield() 暂停当前正在执行的线程对象,并执行其他线程.( 礼让 )
void interrupt() 中断线程。(不建议使用)
boolean isAlive() 测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。

不推荐使用、 stop()、destroy() 方法

  • 最好让线程自然停止
  • 建议用一个flag 进行中止变量,当 flag = false ,则线程停止运行。

Application 实例

public class Test01 implements Runnable {
    // 1. 线程中定义线程体使用的 flag 标识位
    private boolean flag = true;
    
    @Override
    public void run() {
        // 2. 在线程中使用该标识位
        while(flag){
            System.out.println("Run、、、Thread");
        }
    }
    
    // 3. 对外提供方法改 flag
    public void stop(){
        
        this.flag = false;
        
    }
}

标志位停止线程

Application

package Thread;

// 测试stop

/**
 *  1. 建议线程正常停止--> 利用次数,不建议死循环
 *  2. 建议是使用标志位--> 设置一个标志位转换方法进行停止程序
 *  3. 不使用过时方法
 */
public class TestStop implements Runnable {

    // 1. 设置一个标识符
    private boolean flag = true;


    @Override
    public void run() {
        int i = 0;
        while(flag){
            System.out.println("Run、、、Thread"+i++);
        }
    }
    // 2. 设置一个公开的方法停止线程,转换标志位
    public void stop(){
        this.flag = false;
    }
    public static void  main(String[]args){
        TestStop testStop = new TestStop();
        new Thread(testStop).start();

        for (int i = 0; i < 1000; i++) {

            System.out.println("main"+i);

            if (i==900){
                testStop.stop();
                System.out.println("该线程停止了");

            }
        }
    }
}

线程休眠( sleep )

  • sleep 存在异常需要抛出
  • sleep 可以模拟网络延时,倒计时等等
  • 每一个对象都有一个锁,sleep 不会释放锁

// 模拟网络延时: 
// 现线程不安全 
// 放大问题的发生性
            // 模拟延时
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
// 在抢火车票的时候体现出了这一点 出现负数的票

模拟倒计时

Application

package Thread;

// 模拟倒计时、、、
public class TestSleep02 {
    public static void main(String[] args) {
        try {
            tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static  void tenDown() throws InterruptedException{

        int num = 10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if (num==0){
                break;
            }
        }

    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestSleep02
10
9
8
7
6
5
4
3
2
1
Process finished with exit code 0


Application

public static void main(String[] args)  throws Exception {
// 每隔一秒  打印一次当前系统时间
    Date startTime = new Date(System.currentTimeMillis());// 获取系统当前时间
    
    while (true){
        
            Thread.sleep(1000);
            
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
            
            startTime = new Date(System.currentTimeMillis());// 更新当前时间  要不然跑的不对
            
      
    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestSleep02
21:06:53
21:06:54
21:06:55
21:06:56
21:06:57
21:06:58
21:06:59
​`````````

Process finished with exit code -1


线程礼让(yield)

礼让线程,让当前正在运行的线程暂停,但不阻塞

将线程从运行状态转为就绪状态(A让出来和B一起重新跑)

让CPU 重新调度,礼让不一定成功!看 CPU 心情


Application

package Thread;

// 测试礼让线程
// 礼让线程不一定成功,看 CPU 心情
public class TestYield {
        public static void main(String[]args){

            MyYield myYield = new MyYield();

            new Thread(myYield,"A").start();

            new Thread(myYield,"B").start();
        }
        
}

class MyYield implements Runnable{
    public void run(){
        /**
         * 如果没有礼让  线程开始然后停止是连一起的,有了礼让呢,就会退出来让 B 运行 然后再停止
         * 但是实际测试 并不能达到预期情况 总之 礼让就是这个意思  不必在意这个
         */
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        //Thread.yield(); // 礼让 如果礼让成功 下面不会打印
        System.out.println(Thread.currentThread().getName()+"程序停止执行");
    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestYield
A线程开始执行
B线程开始执行
A程序停止执行
B程序停止执行

Process finished with exit code 0


线程插队( Join )

  • Join 合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
  • 可以想象插队, 插队嘛 当然要在人家没进内存的时候才插队,要不然就来不及了赛 ( 紧跟在 start 后面 霸道的很!)

Application

package Test;

public class TestJoin implements Runnable {
    public static void main(String[] args) throws InterruptedException { // 中断异常

        TestJoin join = new TestJoin();
        Thread thread = new Thread(join);
        thread.start();

        for (int i = 0; i < 100 ;i++) {
            if (i==50){
                thread.join(); // main 线程阻塞 当 i = 50 main 线程阻塞 直到 join 跑完
                // 就像插队
            }
            System.out.println("main --->"+i);
        }
    }
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("join --->"+i);
        }
    }
}


线程状态( state )

State 是 Thread 的一个静态嵌套类 所以用 class. State 调用




Application

package Thread;

// 观察测试线程的状态
public class TestState {
    public static void main(String[] args)  throws InterruptedException{
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5 ; i++) {

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }


            System.out.println("、、、、、、");
        });

        // 观察状态
        Thread.State state = thread.getState();
        System.out.println(state);

        // 观察启动后
        thread.start(); // 启动线程
        state = thread.getState();
        System.out.println(state);// Run

        while(state!=Thread.State.TERMINATED){ // 只要线程不终止,就一直输出状态
            Thread.sleep(100);
            state = thread.getState(); // 要更新线程状态 这里更新的值 是给 while 去判断用的
            System.out.println(state); // 输出状态
        }
        // 线程只能启动一次 ,死亡后不能再次启动 会报错
        thread.start();
        

    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestState
NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
略、、
TIMED_WAITING
TIMED_WAITING
RUNNABLE
、、、、、、
TERMINATED
Exception in thread "main" java.lang.IllegalThreadStateException
	at java.base/java.lang.Thread.start(Thread.java:794)
	at Thread.TestState.main(TestState.java:34)

Process finished with exit code 1


线程优先级(Priority)

       t2.setPriority(1);// 和join 相反 先设置优先级 再启动
        t2.start();

  • JAVA提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行

  • 线程的优先级用数字表示,范围从1~10

    • Thread.MIN_Priority = 1;
    • Thread.Max_Priority = 10;
    • Thread.Norm_Priority = 5;
  • getPriority() setPriority ( int i );

    最终的结果还是看 CPU 心情,优先级高不一定先执行,只是先执行的概率大一些

package Thread;


public class TestPriority {
    public static void main(String[] args) {
        // 主线程默认优先级 是改不了的
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority,"1");
        Thread t2 = new Thread(myPriority,"2");
        Thread t3 = new Thread(myPriority,"3");
        Thread t4 = new Thread(myPriority,"4");
        Thread t5 = new Thread(myPriority,"5");
        Thread t6 = new Thread(myPriority,"6");

        // 设置优先级 再启动

        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(6);
        t3.start();


        t4.setPriority(7);
        t4.start();


        t5.setPriority(Thread.MAX_PRIORITY); // 最大优先级 = 10
        t5.start();

        t6.setPriority(8);
        t6.start();

    }
}

class MyPriority implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestPriority
main--->5
5--->10
4--->7
6--->8
1--->5
3--->6
2--->1

Process finished with exit code 0

守护线程 (Daemon)

  • 线程分为用户线程和守护线程

  • 虚拟机必须确保用户线程执行完毕

  • 虚拟机不用等待守护线程执行完毕

    这句要和上面一句一起理解,虚拟机不确保守护线程执行到结束,只负责用户执行到结束,一旦守护线程所守护的用户线程结束,那么可能会有一定延时后结束守护线程。

  • 如;后台记录操作日志,监控内存,垃圾回收等待机制

Application

package Thread;

public class TestDaemon {

    public static void main(String[] args) {

        God god = new God();
        You01 you = new You01();

        Thread thread = new Thread(god);
        thread.setDaemon(true); // 默认是 false 是用户线程,正常的线程都是用户线程

        thread.start(); // 上帝  守护线程 启动

        new Thread(you).start(); // 你 用户线程

    }
}

// 上帝
class God implements Runnable{
    @Override
    public void run() {
      while(true){
          System.out.println("上帝保佑着你");
      }
    }
}

// 你
class You01 implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 30000; i++) {
            System.out.println("你一生都开心的活着");
        }
        System.out.println("=====goodbye World=======");
    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestDaemon
上帝保佑着你
你一生都开心的活着
你一生都开心的活着
上帝保佑着你
上帝保佑着你
上帝保佑着你
你一生都开心的活着
你一生都开心的活着
=====goodbye World=======
上帝保佑着你 // 守护线程在用户线程终止后还会有延时后关闭线程
上帝保佑着你
上帝保佑着你
Process finished with exit code 0


7——线程同步(Synchronized-同步锁)

多个线程操作一个资源

  • 并发 ; 同一个对象被多个线程同时操作 | 多个线程访问同一个对象

    • 上万人抢十张票
    • 两个银行同一个银行卡同时取钱
  • 解决 ; 进行排队 在程序中叫队列

  • 线程同步; 是一个等待机制,多个需要访问的线程,进入这个 对象的等待池 形成队列,等待前面的线程使用完毕,下一个线程再使用 线程同步 = 队列+锁


  • 由于同一进程的多个线程共享同一块储存空间,在带来方便的同时,也带来了访问冲突的问题,为了,保证数据在方法中被访问时的正确性,在访问时,加入锁机制 synchronized,当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可,存在以下问题
    • 一个线程持有锁会导致其他所有需要此锁的线程挂起;(也就是进入等待状态)
    • 在多个线程竞争下加锁,释放锁,会导致比较多的上下文切换 和 调度延时,引起性能问题 (理解为使程序变的繁琐,影响执行效率)
    • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题

例 1 不安全的买票

Application

package Thread.syn;

// 不安全的买票
//  出现了负数 和多出一张票 等情况 说明线程不安全

public class UnsafeBuyTicket {
    public static void main(String[] args) {

        BuyTicket station = new BuyTicket();

        Thread t1 = new Thread(station,"阿珍");
        Thread t2 = new Thread(station,"阿强");
        Thread t3 = new Thread(station,"我");
        Thread t4 = new Thread(station,"阿牛");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

class BuyTicket implements Runnable{


    // 票
    private int ticketNums = 10 ;
    boolean flag =  true; // 外部停止方式

    @Override
    public void run() {
        // 买票
        while(flag){
            buy();
        }

    }

    private void buy(){
        // 模拟延时
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 判断是否有票
        if ( ticketNums <=0){
            flag = false;
           return; // return 不带返回值 退出该方法
            
        }
        // 买票
        System.out.println(Thread.currentThread().getName() + "拿到"+ticketNums--);
        
    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.syn.UnsafeBuyTicket
阿珍拿到9
阿牛拿到10
阿强拿到8
我拿到10
阿珍拿到7
阿牛拿到6
阿强拿到5
我拿到4
我拿到3
阿牛拿到1
阿强拿到2

Process finished with exit code 0


例 2 不安全的银行

Application

package Thread.syn;

// 不安全的银行
// 两个人取银行取同一个账户
public class UnsafeBank {
    public static void main(String[] args) {
        // 账户
        Account account = new Account(100,"结婚基金");

        Drawing you = new Drawing(account,50,"你");
        Drawing girlFried = new Drawing(account,100,"girlFried");

        you.start();
        girlFried.start();

    }

}

class  Account {
    int Money; // 余额
    String name; // 卡名

    public Account(int money, String name) {
        Money = money;
        this.name = name;
    }
}

// 银行 : 模拟取款
class Drawing extends Thread{ // Drawing 画画
    Account account; // 账户

    // 取了多少前
    int drawingMoney ;
     // 现在手里多少钱
    int nowMoney;

    public Drawing(Account  account , int drawingMoney,String name){
        super(name);// 传给父类 Thread 相当于给线程 取名字
        this.account = account;
        this.drawingMoney = drawingMoney;


    }
    // 取钱
    @Override
    public void run() {
        
        // 判断有没有钱
        if (account.Money-drawingMoney<0){
            System.out.println(Thread.currentThread().getName()+"钱不够了 取不了!");
            return;
        }

        // sleep 提高放大问题的发生性 这里加 sleep 就是 两个人都到这个位置等待
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 卡内余额 = 余额 - 你取的钱
        account.Money = account.Money-drawingMoney;
        // 你手里的钱
        nowMoney = nowMoney + drawingMoney;

        System.out.println(account.name+"余额为:"+account.Money);
        // Thread.currentThread().getName() = this.getName()  继承了 Thread 可以用它的所有方法 可以简化
        System.out.println(this.getName()+"手里的钱: "+nowMoney);
    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.syn.UnsafeBank
结婚基金余额为:-50
结婚基金余额为:0
你手里的钱: 50
girlFried手里的钱: 100

Process finished with exit code 0


例 3 不安全的集合

Application

package Thread.syn;

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

// 线程不安全集合
public class UnsafeList {
    public static void main(String[] args) {

        List<String> list = new ArrayList<String>();

        for (int i = 0; i <10000 ; i++) {
            // 因为 Thread 只有一个抽象方法 run 所以可以用 lambda 表达式
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        // 提高问题的发生性,等待计数完后打印
        // 大概意思 : 两个线程 添加到了 list 的同一个地址 就会覆盖所以导致不安全

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(list.size());

    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.syn.UnsafeList
9985

Process finished with exit code 0


同步方法 ( Synchronized )

  • 方法里面需要修改的内容才需要锁,锁的太多,浪费资源
// 在方法前加 Synchronized  同步方法
Synchronized  void  buy(){
     
 }
private Synchronized void buy(){
    // 模拟延时
    try {
        Thread.sleep(200);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    // 判断是否有票
    if ( ticketNums <=0){
        flag = false;
       return; // return 不带返回值 退出该方法
        
    }
    // 买票
    System.out.println(Thread.currentThread().getName() + "拿到"+ticketNums--);
    
}

同步块 Synchronized ( Obj )

// 锁的对象就是变化的量.需要增删改的量 (obj)
 Synchronized  ( Obj ){
 
   }

  // 取钱
    @Override
    public void run() {
        
        Synchronized(account){
            
        // 判断有没有钱
        if (account.Money-drawingMoney<0){
            System.out.println(Thread.currentThread().getName()+"钱不够了 取不了!");
            return;
        }

        // sleep 提高放大问题的发生性 这里加 sleep 就是 两个人都到这个位置等待
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 卡内余额 = 余额 - 你取的钱
        account.Money = account.Money-drawingMoney;
        // 你手里的钱
        nowMoney = nowMoney + drawingMoney;

        System.out.println(account.name+"余额为:"+account.Money);
        // Thread.currentThread().getName() = this.getName()  继承了 Thread 可以用它的所有方法 可以简化
        System.out.println(this.getName()+"手里的钱: "+nowMoney);
            
            
            
        }
        
    }

扩充 CopyOnWriteArrayList 先不管它

package Work;

import java.util.concurrent.CopyOnWriteArrayList;

// 测试 JUC 安全类型的集合
public class TestJUC {

    public static void main(String[] args)  throws Exception{

        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }

        // 原本打印  8845  因为没有统计完全就打印了 所以 这里留点时间统计数据
        Thread.sleep(3000);

        System.out.println(list.size());

    }

}

8 —— 死锁 ( deadLock )

死锁 : 多个线程互相抱着对方需要得资源, 然后形成僵持

Application

package Thread;

public class DeadLock {

    public static void main(String[] args) {

        Makeup makeup00 = new Makeup(1,"灰姑凉");
        Makeup makeup01 = new Makeup(0,"白雪公主");
        makeup00.start();
        makeup01.start();

    }

}

// 口红
class  Lipstick {

}

// 镜子
class Mirror{

}

class  Makeup extends Thread {

    // 需要得资源只有一份. 用 static 来保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();

    int choice; // 选择
    String girlName; // 使用化妆品的人

    public Makeup(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        try {

            makeup();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 化妆 , 互相持有对象的锁,就是需要拿到对方的资源
    // 死锁 : 双方都把对方需要的锁了,并切还互想需要,就阻塞了, 就是锁中锁,
    private void makeup() throws InterruptedException{

        if (choice == 0){

            synchronized (lipstick){ // 获得口红的锁
                System.out.println(this.girlName+"获得口红的锁");
                Thread.sleep(1000);

            }
            
            
            // 只要把锁拿出来,避免锁中锁就可以了
            
             /**
             * synchronized (mirror){// 一秒钟后想拿镜子的锁
             *   synchronized (mirror){
             *   
             *    }
             * }
             */
            
            synchronized (mirror){// 一秒钟后想拿镜子的锁
                System.out.println(this.girlName+"获得镜子的锁");
            }

        }else {
            synchronized(mirror){// 获得镜子的锁
                System.out.println(this.girlName+"获得镜子的锁");
                Thread.sleep(2000);

            }
            synchronized (lipstick){ // 一秒钟后想拿口红的锁
                System.out.println(this.girlName+"获得口红的锁");
            }
        }

    }

}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.DeadLock
白雪公主获得口红的锁
灰姑凉获得镜子的锁
白雪公主获得镜子的锁
灰姑凉获得口红的锁

Process finished with exit code 0


9——Lock (锁)

  • java.util.concurrent.lock.Lock 接口是控制多个线程对共享资源进行访问的工具.锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得 Lock 对象.
  • ReentrantLock 类实现了Lock , 它拥有与 synchronized 相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是 ReentrantLock , 可以显式加锁,释放锁.

使用 lock 需要配合 try finally 使用

Application

try {

    lock.lock(); // 加锁
    if (ticketNums>0){
        try {
            Thread.sleep(100);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(ticketNums--);

    } else {
        break;
    }

} finally {
    
    lock.unlock(); // 释放锁
}

Application

package Thread;

import java.util.concurrent.locks.ReentrantLock;

// 测试 Lock 锁
public class TestLock {
    public static void main(String[] args) {

        TestLock2 testLock = new TestLock2();

        new Thread(testLock,"1").start();
        new Thread(testLock,"2").start();
        new Thread(testLock).start();

    }

}

class TestLock2 implements Runnable{

    int ticketNums = 10 ;

    // 定义 lock 锁
    private ReentrantLock lock = new ReentrantLock( );

    @Override
    public void run(){

        while (true) {

            try {

                lock.lock(); // 加锁
                if (ticketNums>0){
                    try {
                        Thread.sleep(100);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNums--);

                } else {
                    break;
                }

            } finally {
                lock.unlock();
            }

        }

    }
}


10 ——生产者和消费者问题 ( 线程协作问题 )

把原本互不影响的线程, 进行一个协调协作的操作

  • Java 提供了几个方法解决线程之间的通信问题
方法名 作用
wait() 表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
wait(long timeout) 指定等待的毫秒数
notify 唤醒一个处于等待状态的线程
notifyAll 唤醒同一个对象上所有调用 wait() 方法的线程,优先级别高的线程优先调度

注意 : 均是 Oject 类不是Thread 方法,都只能在同步方法或者同步代码块中使用,非则会抛出异常 IllegalMonitorStateException

两个解决方法

管程法

管程法: 在生产者和消费者中间建立一个缓冲区,用缓冲区来建立方法解决协作问题

Application

package Thread;

// 测试 : 生产者消费者模型--> 利用缓冲区解决 :  管程法

// 生产者, 消费者,产品,缓冲区
public class TestPC {

    public static void main(String[] args) {

        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}

// 生产者
class Productor extends Thread{

    SynContainer container ;
    public Productor(SynContainer container) {
        this.container = container;
    }

    // 生产
    @Override
    public void run() {
        // 准备生产100 只鸡
        for (int i = 0; i < 100 ; i++) {
            container.push(new Chicken(i));
            System.out.println("生产了"+i+"只鸡");

        }
    }
}

// 消费者
class Consumer extends Thread{
    SynContainer container ;

    public Consumer(SynContainer container) {
        this.container = container;
    }

    // 消费
    @Override
    public void run() {
   
        for (int i = 0; i < 100 ; i++) {
            System.out.println("消费了-->"+container.pop().id+"只鸡");
        }

    }

}

// 产品
class Chicken{
    int id; // 产品编号
    public Chicken(int id) {
        this.id = id;
    }
}

// 缓冲区
class SynContainer {

    // 需要一个容器大小
    Chicken[] chickens = new Chicken[10];

    // 容器计数器
    int count = 0;

    // 生产者放入产品
    public synchronized void push(Chicken chicken){
        // 容器满了, 就需要等待消费者消费
        if (count == chickens.length){
            // 通知消费者消费 . 生产者等待
            try {
                this.wait(); // wait() 是一个Object 方法,在被唤醒前,调用该方法(对象)的线程一直等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        // 如果没有满,我们就需要丢入产品
        chickens[count] = chicken;
        count++;

        // 可以通知消费者消费了
        this.notifyAll();
    }

    // 消费者消费产品
    public synchronized Chicken pop(){
        // 判断能否消费
        if (count== 0){
            // 等待生产者生产,
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        // 如果可以消费
        count--;
        // 象征性的把鸡拿走 这里是同步数据,count++对应count-- 就是同一个对象 
        // 再者 count 与下行并不能对调位置,因为chickens[1]的鸡还没有做出来,会显示空指针异常 null
        Chicken chicken = chickens[count];

        // 吃完了 通知生产者生产
        this.notifyAll();
        return chicken;

    }

}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestPC
生产了0只鸡
生产了1只鸡
生产了2只鸡
生产了3只鸡
生产了4只鸡
生产了5只鸡
生产了6只鸡
生产了7只鸡
生产了8只鸡
生产了9只鸡
生产了10只鸡
消费了-->9只鸡
生产了11只鸡
消费了-->10只鸡
生产了12只鸡
消费了-->11只鸡
生产了13只鸡
消费了-->12只鸡
生产了14只鸡
消费了-->13只鸡
......


Process finished with exit code 0


信号灯法

信号灯法 : 就是用取反的标志位来解决问题

Application

package Thread;
// 测试生产者消费者问题2: 信号灯法,标志位解决

public class TestPc2 {
    public static void main(String[] args) {
        TV tv = new TV();

        new Player(tv).start();
        new Watcher(tv).start();
    }

}

// 生产者--> 演员
class Player extends Thread {
    TV tv;

    public Player(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            if (i%2==0){
                this.tv.play("快乐大本营播放中...");
            }else {
                this.tv.play("抖音: 记录美好生活~");
            }
        }
    }
}

// 消费者 --> 观众
class Watcher extends Thread{
    TV tv;

    public Watcher(TV tv) {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            this.tv.watch();
        }
    }
}

// 产品 --> 节目
class TV {
    // 演员表演,观众等待 T
    // 观众观看,演员等待 F
    String voice; // 表演的节目
    boolean flag = true;

    // 表演
    public synchronized void play(String voice){
        if (!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了"+voice);
        // 通知观众观看
        this.notifyAll();// 通知唤醒
        this.voice = voice; // 声音跟新 让观众能看见
        this.flag = !this.flag; // 取反

    }
    // 观看
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("观看了: "+voice);
        // 通知演员表演
        this.notifyAll();
        this.flag = !this.flag;

    }

}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestPc2
演员表演了快乐大本营播放中...
观看了: 快乐大本营播放中...
演员表演了抖音: 记录美好生活~
观看了: 抖音: 记录美好生活~
演员表演了快乐大本营播放中...
观看了: 快乐大本营播放中...
演员表演了抖音: 记录美好生活~
观看了: 抖音: 记录美好生活~
演员表演了快乐大本营播放中...
.......

Process finished with exit code 0


11——线程池 (理解)

  • 背景: 经常创建和销毁, 使用量特别大的资源, 比如并发情况下的线程, 对性能影响很大

  • 思路 : 提前创建好多个线程, 放入线程池中, 使用时,直接获取, 使用完放回池中, 可以避免频繁创建销毁,实现重复利用, 类似生活中的公共交通工具

  • 好处 :

    • 提高相应速度 (减少了创建新线程的时间)
    • 降低资源消耗 ( 重复利用线程池中线程, 不需要每次创建)
    • 便于线程管理
      • corePoolSize : 核心池的大小
      • maximumPoolSize : 最大线程数
      • keepAliveTime : 线程没有任务时最多保持多长时间后终止.
  • JDK 5.0 起提供了 线程池相关API : ExecutorService 和 Executor

  • ExecutorService : 真正的线程池接口,常见子类 ThreadPoolExecutor

    • void execute(Runnable command) : 执行任务|命令 , 没有返回值,一般用来执行 Runnable
    • Future submit (Callabletask) : 执行任务, 有返回值,一般又来执行 Callable
    • void shutdown() : 关闭连接池
  • Executors : 工具类.线程池的工厂类,用于创建并返回不同类型的线程池


Application

package Thread;


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

public class TestPool {

    public static void main(String[] args) {
        
        // 1. 创建服务,创建线程池
        // newFixedThreadPool 参数为:线程池的大小
        ExecutorService service = Executors.newFixedThreadPool(10);

        // 执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        // 2. 关闭连接
        service.shutdown();

    }

}

class MyThread implements Runnable{

    @Override
    public void run() {
            System.out.println(Thread.currentThread().getName());

    }
}

Print

F:\Application\out\production\Application;F:\Application\src\Lib\commons-io-2.11.0.jar Thread.TestPool
pool-1-thread-2
pool-1-thread-1
pool-1-thread-4
pool-1-thread-3

Process finished with exit code 0

新增单词

0 create Library 创建库 莱伯乱
1 Project structure 项目结构 丝架(tr)可却
2 ticket 踢给特
3 Race 赛跑 乱丝
4 winner 胜利者 winner
5 gameover 游戏结束
6 steps 步数 丝打破丝
7 commons io 下载工具类库 康芒丝 io
8 FileUtils.copyURLToFile 文件工具类 . 复制URL到文件
9 StaticProxy 静态代理模式
10 Marry 结婚 马瑞
11 weddingCompany 婚庆公司
12 target 目标 托get
13 Lambda 希腊字母11位 兰布达 λ
14 yield 礼让 有的
15 join 加入 插队
16 state 线程状态 丝得特 stat 状态 丝打特
17 Runnable 正在运行
18 Blocked 阻塞
19 Waiting 无限期的等待 威丁
20 Timed Waiting 有时间的等待
21 Terminated 终止状态 檀木内特的
22 Priority 优先级 派or润踢
23 Norm 正常
24 Daemon 守护线程 打们
25 Synchronized 同步 森q纳斯的
26 Unsafe 不安全的 按塞夫
27 station 车站
28 Account 账号 额康特
29 girlFriend 女朋友
30 DeadLock 死锁
31 Lipstick 口红 类噗丝得k
32 Mirror 镜子 妹弱啊
33 Makeup 化妆
34 choice 选择(cho) 雀氏
35 ReentrantLock 可重入锁 润tr lock
36 unlock 解锁
37 Productor 生产者 噗丢塞
38 Consumer 消费者 肯素们
39 Chicken
40 wait 等待
41 notifyAll 通知 | 唤醒
42 puth 放入(应该是一种缩写) 噗徐
43 pop 取出(应该是一种缩写)
44 Player 演员(选手)
45 Watcher 观众
46 watch 观看
47 voice 声音
posted @ 2021-12-06 22:24  项晓忠  阅读(38)  评论(0编辑  收藏  举报