十一、多线程

多线程

程序、进程、线程

程序:程序是指令和数据的有序集合,是一个静态的概念

进程:执行程序的一次执行过程,是一个动态的概念,进程是资源分配的单位

线程:一个进程中有若干个线程,线程是CPU调度和执行的单位

如:视频中同时可以听声音、看图像、看弹幕

线程的创建

Thread

package com.dy.demo01;

//创建线程方式一:继承Thread类,重写run方法,调用start开启线程
public class TestThread extends Thread{
    //run方法线程体
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码---"+i);
        }
    }

    //main线程,主线程
    public static void main(String[] args) {
        //创建一个线程对象
        TestThread testThread = new TestThread();
        //调用start()开启线程
        testThread.start();
        for (int i = 0; i < 200; i++) {
            System.out.println("我在学习---"+i);
        }
    }
}

Runnable

package com.dy.demo01;

//创建线程方法2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法
public class TestThread2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码--"+i);
        }
    }

    public static void main(String[] args) {
        //创建runnable接口的实现类对象
        TestThread2 testThread2 = new TestThread2();
        //创建线程对象,通过线程对象来开启我们的线程
        new Thread(testThread2).start();

        for (int i = 0; i < 200; i++) {
            System.out.println("我在学习多线程--"+i);
        }
    }
}

问题

数据紊乱

package com.dy.demo01;

//多个线程操作同一个对象
//问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class TestThread3 implements Runnable{

    private int target=10;

    @Override
    public void run() {
        while (true){
            if (target<=0){
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"=>拿了第"+target--+"张票!");
        }
    }

    public static void main(String[] args) {
        TestThread3 target = new TestThread3();
        new Thread(target,"小明").start();
        new Thread(target,"老师").start();
        new Thread(target,"黄牛").start();
    }
}

结果

老师=>拿了第9张票!
小明=>拿了第10张票!
黄牛=>拿了第8张票!
黄牛=>拿了第7张票!
老师=>拿了第5张票!
小明=>拿了第6张票!
小明=>拿了第4张票!
老师=>拿了第4张票!
黄牛=>拿了第4张票!
小明=>拿了第3张票!
黄牛=>拿了第2张票!
老师=>拿了第3张票!
黄牛=>拿了第0张票!
老师=>拿了第1张票!
小明=>拿了第1张票!

龟兔赛跑

package com.dy.demo01;

public class TestThread4 implements Runnable{

    private static String winner;

    @Override
    public void run() {
        for (int i = 0; i <=100; i++) {
            if (Thread.currentThread().getName()=="兔子" && i%50==0){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            if (gameOver(i)){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
        }
    }

    private boolean gameOver(int steps){
        if (steps>=100){
            winner=Thread.currentThread().getName();
            System.out.println("winner is "+winner);
        }
        if (winner!=null){
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        TestThread4 testThread4 = new TestThread4();
        new Thread(testThread4,"兔子").start();
        new Thread(testThread4,"乌龟").start();
    }
}

Callable(了解)

package com.dy.demo01;

import java.util.concurrent.*;

/**
 * 可以抛出返回值
 * 可以定义异常
 */
//实现Callable接口
public class TestThread5 implements Callable<Boolean> {
    @Override
    public Boolean call() throws InterruptedException {
        for (int i = 0; i < 1000; i++) {

            System.out.println("我在看代码---"+i);
        }
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestThread5 testThread5 = new TestThread5();
        //创建执行服务
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        //提交执行
        Future<Boolean> submit1 = executorService.submit(testThread5);
        Future<Boolean> submit2 = executorService.submit(testThread5);
        //获取结果
        Boolean aBoolean1 = submit1.get();
        Boolean aBoolean2 = submit2.get();
        //关闭服务
        executorService.shutdownNow();
    }
}

静态代理

package com.dy.demo02;
//静态代理模式总结
//真实对象(目标对象)和代理对象实现同一个接口
//代理对象要代理真实角色
//好处
//  代理对象可以做很多真实对象做不了的事情
//  真实对象专注做自己的事情
public class StaticProxy {
    public static void main(String[] args) {
        WeddingCompany company= new  WeddingCompany(new You());
        company.happyMarry();
    }
}

interface Marry{
    void happyMarry();
}
//真实角色,你去结婚
class You implements Marry{

    @Override
    public void happyMarry() {
        System.out.println("你结婚了,你很开心!");
    }
}
//代理角色,帮你结婚
class WeddingCompany implements Marry{

    private Marry marry;

    public WeddingCompany(Marry marry) {
        this.marry=marry;
    }

    @Override
    public void happyMarry() {
        before();
        marry.happyMarry();
        after();
    }

    private void after() {
        System.out.println("打扫结婚场地");
    }

    private void before() {
        System.out.println("收结婚礼金");
    }
}

Lambda表达式

  1. 使代码变得更简洁
  2. 去掉一些没有用的代码,只留下核心代码
  3. 避免内部类定义过多

函数式接口:一个接口只有一个方法

public interface Runnable{
    public abstract void run();
}

函数式接口可以使用Lambda表达式

package com.dy.demo02;

/**
 *推导lambda表达式
 */
public class Lambda {
    //3.静态内部类
    static class Like2 implements Ilike{
        @Override
        public void Lambda() {
            System.out.println("I like lambda2!");
        }
    }

    public static void main(String[] args) {
        Ilike like1 = new Like1();
        like1.Lambda();

        Ilike like2 = new Like2();
        like2.Lambda();
        //4.局部内部类
        class Like3 implements Ilike{
            @Override
            public void Lambda() {
                System.out.println("I like lambda3!");
            }
        }

        Like3 like3 = new Like3();
        like3.Lambda();
        //5.匿名内部类,没有类的名称,借助接口或者父类
        Ilike like4= new Ilike() {
            @Override
            public void Lambda() {
                System.out.println("I like lambda4!");
            }
        };
        like4.Lambda();

        //6.lambda表达式简化
        Ilike like5=()->{
            System.out.println("I like lambda5!");
        };
        like5.Lambda();
    }
}

//1.定义一个函数式接口
interface Ilike{
    void Lambda();
}

//2.实现类
class Like1 implements Ilike{

    @Override
    public void Lambda() {
        System.out.println("I like lambda1!");
    }
}
package com.dy.demo02;

public class Lambda2 {
    public static void main(String[] args) {
         
        //1.简化1,去掉返回值类型
        Ilove ilove=(i)->{
            System.out.println("I love you -->"+i);
        };

        ilove.love(1000);
        //2.简化2 简化括号
        ilove=i->{
            System.out.println("I love you -->"+i);
        };
        ilove.love(10000);
        //3.简化3,去掉花括号  
        ilove=i-> System.out.println("I love you -->"+i);
        ilove.love(100000);
        //总结:
        //lambda表达式只能有一行代码的情况下,才能简化成为一行,如果有多行,就用代码块包裹
        //必须是函数式接口
        //多个参数,也可以去掉参数类型,必须加上括号
    }
}

interface Ilove{
    void love(int a);
}

多线程详解

五个状态

  1. 创建状态: new Thread()线程对象一旦创建就进入到新生状态
  2. 就绪状态:start() 调用方法后进入,但不意味着立即执行 ,等待CPU的调度执行
  3. 阻塞状态:调用sleep,wait或同步锁定
  4. 运行状态:进入运行状态,线程才真正执行线程体的代码块
  5. 死亡状态:线程中断或者结束,一旦进入死亡,就不能再次启动

线程停止

package com.dy.demo01;

public class TestThread6 implements Runnable{
    private boolean flag=true;
    @Override
    public void run() {
        int i=0;
        while (flag){
            System.out.println("run...Thread "+i++);
        }
    }

    public void stop(){
        this.flag=false;
    }

    public static void main(String[] args) {
        TestThread6 testThread6 = new TestThread6();
        new Thread(testThread6).start();
        for (int i = 0; i < 1000; i++) {
            if (i==900){
                testThread6.stop();
                System.out.println("线程该停止了。。。");
            }
            System.out.println("main..."+i);
        }
    }
}

线程休眠

模拟网络延时:放大问题的发生性

package com.dy.demo01;
//模拟网络延时:放大问题的发生性
public class TestThread7 implements Runnable{
    private int target=10;

    @Override
    public void run() {
        while (true){
            if (target<=0){
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"=>拿了第"+target--+"张票!");
        }
    }

    public static void main(String[] args) {
        TestThread3 target = new TestThread3();
        new Thread(target,"小明").start();
        new Thread(target,"老师").start();
        new Thread(target,"黄牛").start();
    }
}

模拟倒计时

package com.dy.demo01;

import java.text.SimpleDateFormat;
import java.util.Date;

//模拟倒计时
public class TestThread8 {
    public static void tenDown() throws InterruptedException {
        int i=10;
        while (true){
            Thread.sleep(1000);
            System.out.println(i--);
            if (i<=0){
                break;
            }
        }
    }

    public static void getCurrentTime() throws InterruptedException {
        Date date = new Date(System.currentTimeMillis());//获取当前系统时间
        while (true){
            Thread.sleep(1000);
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
            date=new Date(System.currentTimeMillis());//获取当前系统时间
        }

    }
    public static void main(String[] args) throws InterruptedException {
        //tenDown();
        getCurrentTime();
    }
}

线程礼让

让CPU重新调度,不一定成功,看CPU

package com.dy.demo01;

public class TestThread9 implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行!");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程停止执行!");
    }

    public static void main(String[] args) {
        TestThread9 testThread9 = new TestThread9();
        new Thread(testThread9,"a").start();
        new Thread(testThread9,"b").start();

    }
}

线程合并

合并线程,待此线程完成后,在执行其他线程,其他线程阻塞

少使用,会阻塞

package com.dy.demo01;

public class TestThread10 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("线程vip..."+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        TestThread10 testThread10 = new TestThread10();
        Thread thread=new Thread(testThread10);
        thread.start();
        for (int i = 0; i < 500; i++) {
            if (i==50){
                thread.join();
            }
            System.out.println("main..."+i);
        }
    }
}

观测线程状态

package com.dy.demo01;

public class TestThread11 {


    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);

        while (state!=Thread.State.TERMINATED){
            Thread.sleep(100);
            state = thread.getState();
            System.out.println(state);
        }
    }
}

线程的优先级

优先级越高,CPU调用的机会越大

package com.dy.demo01;

public class TestThread12 {
    public static void main(String[] args) {
        System.out.println("main-->"+Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);

        t1.start();
        t2.setPriority(3);
        t2.start();
        t3.setPriority(5);
        t3.start();
        t4.setPriority(8);
        t4.start();
        t5.setPriority(7);
        t5.start();

    }
}

class MyPriority implements Runnable{

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

守护线程

daemon

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

虚拟机必须等用户线程执行完毕

虚拟机不用等守护线程执行完毕,只关注用户线程结束后,结束程序

package com.dy.demo01;

public class TestThread13 {
    public static void main(String[] args) {
        God god = new God();
        Thread thread = new Thread(god);
        thread.setDaemon(true);
        thread.start();
        new Thread(new You()).start();
    }
}

class God implements Runnable{

    @Override
    public void run() {
        while (true){

            System.out.println("上帝守护着你!");
        }

    }
}

class You implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("你开心的活着!");
        }
        System.out.println("--you goodbay--");
    }
}

线程同步 synchronized

**并发 **

同一个对象被多个线程同时操作

队列和锁

形成线程同步的条件:队列+锁。

每个对象对应一把锁

线程同步是为了安全

同步方法

synchronized方法和synchronized块

控制对象的访问,每个对象一把锁,方法一旦执行,就独占该锁,知道方法返回释放锁

缺陷:会影响效率

锁的对象为变化的量,增删改

排队买票
package com.dy.demo03;
//不安全的买票
//线程不安全,有重复值,负数
public class UnsafeByTicket {
    public static void main(String[] args) {
        BuyTickets buyTickets = new BuyTickets();
        new Thread(buyTickets,"我").start();
        new Thread(buyTickets,"陌生人").start();
        new Thread(buyTickets,"黄牛").start();

    }
}

class BuyTickets implements Runnable{

    private int ticketNumbers=10;
    boolean flag = true;

    @Override
    public void run() {
        //买票
        while (flag){
            try {
                buy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //synchronized同步方法 拿到this的锁 (Buytickets)
    private synchronized void buy() throws InterruptedException {
        if (ticketNumbers<=0){
            flag=false;
            return;
        }
        Thread.sleep(100);
        System.out.println(Thread.currentThread().getName()+"买到了第"+ticketNumbers--+"张票");
    }
}
银行取钱
package com.dy.demo03;
//不安全的取钱
//两个人去银行取钱
public class UnsafeBank {
    public static void main(String[] args) {
        Account account=new Account(100,"结婚基金");
        Drawing you = new Drawing(account,50,"you");
        Drawing girlFriend = new Drawing(account,100,"girlfriend");
        you.start();
        girlFriend.start();
    }
}
//账户
class Account {
    //余额
     int money;
     //账户名
    
     String name;

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}
//thread:不涉及到多个用户操作同一个对象
class Drawing extends Thread{
    //账户
    private Account account;
    //取了多少钱
    private int drawingMoney;
    //现在手里还剩多少钱
    private int nowMoney;
    public Drawing(Account account,int drawingMoney,String name){
        super(name);
        this.account=account;
        this.drawingMoney=drawingMoney;
    }
    //取钱
    //synchronized默认锁的是this
    //所以这边要用synchronized代码块 同步块可以锁任何东西
    //锁的对象就是变化的量
    @Override
    public  void run() {
        synchronized(account){
            //判断有没有钱
            if (account.money-drawingMoney<0){
                System.out.println(Thread.currentThread().getName()+"钱不够,取不了钱。");
                return;
            }
            //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);
            //this.getName()=Thread.currentThread().getName()
            System.out.println(this.getName()+"手里的钱:"+nowMoney);
        }

    }
}    
集合
package com.dy.demo03;

import java.util.ArrayList;
//线程不安全的集合
public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        ArrayList<String> list = new ArrayList<>();
        //线程插入同一个位置被覆盖
        for (int i = 0; i < 10000; i++) {


                new Thread(()->{synchronized (list){list.add(Thread.currentThread().getName());}}).start();

        }
        Thread.sleep(3000);
        System.out.println(list.size());
    }
}
package com.dy.demo03;

import java.util.concurrent.CopyOnWriteArrayList;

//JUC线程安全的集合
public class TestJUC {
    public static void main(String[] args) throws InterruptedException {
        CopyOnWriteArrayList<String> arrayList = new CopyOnWriteArrayList<>();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{arrayList.add(Thread.currentThread().getName());}).start();
        }
        Thread.sleep(3000);
        System.out.println(arrayList.size());
    }
}

死锁

某一个同步块同时拥有“两个以上对象的锁”,可能产生死锁的问题。

产生死锁的四个必要条件

  1. 互斥条件:一个资源只能被一个进程使用
  2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
  3. 不剥夺条件:进程已获得资源,在未使用完前,不能强行剥夺
  4. 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源的关系
package com.dy.demo03;
//死锁:多个线程互相抱着对方需要的资源,形成僵持
public class DeadLock {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0,"小红");
        Makeup g2 = new Makeup(1,"小黄");
        g1.start();
        g2.start();
    }
}
//口红
class Lipstick{

}
//镜子
class Mirror{

}

class Makeup extends Thread{
    static Lipstick lipstick=new Lipstick();
    static Mirror mirror=new Mirror();
    private int choose;
    private String girlName;
    public Makeup(int choose,String girlName){
        this.choose=choose;
        this.girlName=girlName;
    }
    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
        if (choose==0){
            synchronized (lipstick){//获得口红的锁
                System.out.println(girlName+"获得了口红的锁");
                Thread.sleep(1000);

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

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

        }
    }
}

Lock

显式定义同步锁(手动开启和关闭,别忘记关锁),只有代码块锁

synchronized是隐式锁,出了作用域后自动释放,有代码块和方法锁

ReentrantLock类实现了Lock

package com.dy.demo03;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
        new Thread(testLock2).start();
    }
}
class TestLock2 implements Runnable{
    int ticketNumbers=10;
    //定义Lock 定义私有 常量
    private final ReentrantLock lock=new ReentrantLock();
    @Override
    public void run() {
        while (true){
            lock.lock();//加锁
            try {
                if (ticketNumbers>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(ticketNumbers--);
                }else {
                    break;
                }
            }finally {
                lock.unlock();//解锁
            }

        }
    }
}

线程协作

生产者消费者模式

生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件

生产者:没有生产产品之前,需要通知消费者等待,生产产品后,需要通知消费者消费

消费者:消费后,通知生产者生产新的产品以供消费

synchronized可阻止并发更新同一个共享资源,实现了同步,但不能实现不同线程之间的消息传递

wait() 线程一直等待,直到其他线程通知,会释放锁
wait(long timeout)
notify() 唤醒一个处于等待的线程
notifyAll() 唤醒同一个对象所有wait的线程,优先级高的线程优先调度
package com.dy.demo03;
//测试生产者消费者模型-->利用缓冲区解决 :管程法
public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer=new SynContainer();
        new Productor(synContainer).start();
        new Consumer(synContainer).start();
    }
}
//生产者
class Productor extends Thread{
    SynContainer synContainer;

    public Productor(SynContainer synContainer){
        this.synContainer=synContainer;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            synContainer.push(new Chicken(i));
            System.out.println("生产了第"+i+"只鸡");
        }
    }
}
//消费者
class Consumer extends Thread{
    SynContainer synContainer;

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

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("消费了-->"+synContainer.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();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果没有满,继续丢入产品
        chickens[count]=chicken;
        count++;
        //可以通知消费者消费了
        this.notify();
    }
    public synchronized Chicken pop(){
        //判断能否消费
        if (count==0){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果可以消费
        count--;
        Chicken chicken = chickens[count];
        //吃完了,通知生产者生产
        this.notify();
        return chicken;
    }
}

package com.dy.demo03;
//测试生产者消费者问题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("<1>");
            }else {
                this.tv.play("<2>");
            }
        }
    }
}
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv){
        this.tv=tv;
    }
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}
//产品-->节目
class TV{
    //演员表演,观众等待
    //观众观看,演员等待
    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.voice=voice;
            this.flag=!flag;
            //通知观众观看 通知唤醒
            this.notify();

    }
    public synchronized void watch(){
        if (flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
            System.out.println("观众正在观看:"+voice);
            this.flag=!flag;
            this.notify();

    }
}

线程池

使用线程池,对性能好

提高响应速度

降低资源消耗

便于线程管理

ExecutorService和Executors:

package com.dy.demo03;

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

//测试线程池
public class TestPool {


    public static void main(String[] args) {
        ExecutorService executorService= Executors.newFixedThreadPool(10);
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.shutdown();
    }
}

class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
posted @ 2021-08-18 16:46  盐汽水mua  阅读(47)  评论(0)    收藏  举报