Java多线程


前言

一次只跑一条程序,已经不能满足于我们的日常编程,而我们都能够一遍吃饭一遍看电视,那么程序为什么不能执行着主线程的同时再多跑几条线程呢?


一、多线程

1.进程,线程,多线程

进程:当程序运行的时候就会形成一个进程,而一个进程里面又根据程序的功能可以形成多个线程,每一个线程对应着程序所提供的功能,而当我们同时进行两个线程的时候就是多线程。

2.继承Thread类

在Java程序中如果要实现多线程有三种方法,其为一个类两个接口,类就是Thread类,接口则是Runnable接口和Callable接口。这里主要是通过继承Thread类来进行多线程的操作

示例代码
public class Test2 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 40; i++) {
            System.out.println("我在学习++日语"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException{
        Test2 test2 = new Test2();
        test2.start();

        for (int i = 0; i < 60; i++) {
            System.out.println("我在学习中文"+i);
        }
    }
}

部分结果:
我在学习中文43
我在学习++日语37
我在学习中文44
我在学习++日语38
我在学习中文45
我在学习++日语39

由部分结果我们可以看到,线程不一定立即执行,而是会等待cpu的调用。需要注意的是,开启线程是调用start方法而不是直接调用run()方法

1)同时下载多个网图通过继承Thread类

通过Io工具类的FileUtils.copyURLToFile()方法来把网上的图片下载下来,然后通过多线程进行多张图片的同时下载

示例代码
public class Test2 extends Thread {
        private String url = null;
        private String name = null;

    public Test2(String url,String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {

        DownLoad downLoad = new DownLoad();
        try {
            downLoad.fileDownLoad(url,name);
            System.out.println("已下载了"+name);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Test2 test1 = new Test2("https://i0.hdslb.com/bfs/live/3cbafec446f49594a98fe8becbdc72d644438cf1.jpg@672w_378h_1c.webp","图片1.webp");
        Test2 test2 = new Test2("https://i0.hdslb.com/bfs/archive/d37e475c854e82f1a6c29ad3cfdf6c3bc1dd7887.jpg@672w_378h_1c.webp", "图片2.webp");

        test1.start();
        test2.start();
    }

}



class DownLoad{
    public void fileDownLoad(String url,String name) throws IOException{
        FileUtils.copyURLToFile(new URL(url),new File(name));
    }
}

3.实现Runnable接口

与继承Thread时的代码并没有太大区别。实现Runnable接口来进行多线程的步骤一般是 实现Runnable接口,重写run()方法,通过创建线程对象去启动

示例代码
public class Test2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 40; i++) {
            System.out.println("我在学习++日语"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException{
        Test2 test2 = new Test2();
        new Thread(test2).start();

        for (int i = 0; i < 60; i++) {
            System.out.println("我在学习中文"+i);
        }
    }
}

1)下载多个网图通过实现Runnable接口

不需要修改其他的地方只需要把启动的地方改为创建Thread对象再来使用start方法即可

示例代码
public class Test2 implements Runnable{
    private String url = null;
    private String name = null;

    public Test2(String url,String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {

        DownLoad downLoad = new DownLoad();
        try {
            downLoad.fileDownLoad(url,name);
            System.out.println("已下载了"+name);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Test2 test1 = new Test2("https://i0.hdslb.com/bfs/live/3cbafec446f49594a98fe8becbdc72d644438cf1.jpg@672w_378h_1c.webp","图片1.webp");
        Test2 test2 = new Test2("https://i0.hdslb.com/bfs/archive/d37e475c854e82f1a6c29ad3cfdf6c3bc1dd7887.jpg@672w_378h_1c.webp", "图片2.webp");

        new Thread(test1).start();
        new Thread(test2).start();
    }

}
class DownLoad{
    public void fileDownLoad(String url,String name) throws IOException{
        FileUtils.copyURLToFile(new URL(url),new File(name));
    }
}

2)实现Runnable接口与继承Thread的区别

继承Thread的方法是子类继承Thread类实现多线程能力,而实现Runnable接口则是实现Runnable接口来获得多线程的能力,但是两者启动线程的方法有别,前者是通过子类对象.statrt()来开启多线程,后者则是通过创建一个Thread对象并传入目标对象.statrt()来开启多线程。而实现Runnable接口比继承Thread类的一个好处就是避免了单继承的局限性


4.实现Callable接口

与实现Runnable接口的时候时,从重写run()方法变更到了重写call()方法,开启线程也从new Thread().start()复杂化到了四个步骤

示例代码
//创建执行服务
        ExecutorService pool = Executors.newFixedThreadPool(1);
        //提交执行
        Future<Boolean> submit = pool.submit(test2);
        //返回结果
        Boolean result = submit.get();
        //关闭服务器
        pool.shutdownNow();

5.静态代理

一个类中有一个方法,但是这个方法只能做一件事的时候就可以使用代理。而代理类则比目标类功能强大,但又互不干扰。

示例代码
public class Test2{
    public static void main(String[] args) {
        //
        ProxyComp proxyComp = new ProxyComp(new TargetMen());
        proxyComp.happyMarry();
    }

}


//目标接口
interface Marry{
    void happyMarry();
}

//创建需要代理的类,并实现接口
class TargetMen implements Marry{
    //重写方法
    @Override
    public void happyMarry() {
        System.out.println("代理人");
    }
}



//创建代理类
class ProxyComp implements Marry{
    private TargetMen targetMen;
    //创建有参构造,传入需要代理的类
    public ProxyComp(TargetMen targetMen) {
        this.targetMen = targetMen;
    }

    @Override
    //重写方法
    public void happyMarry() {
        Before();
        System.out.println("========");
        this.targetMen.happyMarry();
        System.out.println("========");
        After();
    }

    //创建代理类做不到的方法
    private void Before(){
        System.out.println("在代理前需要处理的事务");
    }
    private void After(){
        System.out.println("在代理前需要处理的事务");
    }

}

6.Lambda表达式

本质是用来简化匿名内部类的,其使用条件是必须位函数接口,且接口中的方法不多于一个

未使用lambda表达式示例代码
public class Test2{
    public static void main(String[] args) {
        //使用匿名方式实现接口
        Marry marry =new Marry(int a) {
            @Override
            public void happyMarry(int a) {
                System.out.println("我是匿名"+a);
            }
        };
        marry.happyMarry(2);
    }
}

//目标接口
interface Marry{
    void happyMarry(int a);
}

使用lambda表达式示例代码

public class Test2{
    public static void main(String[] args) {
        //使用lambda表达式
        Marry marry =a-> System.out.println("我是匿名"+a);
        marry.happyMarry(2);
    }
}
//目标接口
interface Marry{
    void happyMarry(int a);
}

7.线程的五个状态

在这里插入图片描述

1)线程的新生态

当我们创建线程的时候,这时候就是处于新生状态

2)线程的就绪态

当我们使用.start()方法时为就绪态

3)线程的运行态

线程获取cpu的权限开始运行的时候为运行态

4)线程的阻塞态

当线程因为某种原因放弃cpu的使用权的时候,暂停运行,此时为阻塞态。而阻塞态有三种情况:

1、等待阻塞:通过调用线程的wait()方法,让线程等待某工作的完成

2、同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态

3、其他阻塞:调用了sleep()或join()时,或发出了I/O请求时候。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。

5)线程的死亡态

当线程运行完成就进入了死亡态,转让cpu资源,让其他线程使用cpu

8.观测线程状态

上面已经讲述了线程的五个状态,而现在就通过实际代码来更直观的查看线程从新生到死亡的状态

示例代码
public class Test1 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("啊哈哈哈线程等待咯~"+i);
        }
    }
}

class ThreadStart{
    public static void main(String[] args) throws InterruptedException{
        Test1 test1 = new Test1();
        Thread thread = new Thread(test1);
        //创建线程,此时为新生态
        System.out.println(thread.getState());

        //线程启动
        thread.start();
        //此时为进行态
        Thread.State state = thread.getState();
        System.out.println(state);

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

        //线程执行完成,死亡
        System.out.println(thread.getState());

    }
}

1)线程的停止

在Java中我们查看Thread 的源码的时候,我们可以看到stop方法已经被废弃了,所以自然状态下,要结束一个线程只能等其自动完成后停止。当然我们也可以手动变更标识位的值来结束一个线程

示例代码
public class Test2 implements Runnable{
    //创立标识位并设置为活跃状态
    private boolean flag = true;

    @Override
    public void run() {
        int i=0;
        while(flag){
            System.out.println(Thread.currentThread().getName()+"线程运行在"+i++);
        }
    }

//创建停止线程方法
    public void stop(){
    //将标识位改为停止态
        this.flag = false;
    }

    public static void main(String[] args) {

        Test2 test2 = new Test2();
        //创建一个新进程
        new Thread(test2,"线程1").start();
        //开始一千次循环,当循环到900次的时候终止循环
        for (int i = 0; i <1000 ; i++) {
            System.out.println("main"+i);
            if(i==900){
            test2.stop();
            }
        }
    }
}

2)线程的阻塞–sleep

通常是让线程进入假堵塞的状态,比如模拟接收信息需要延迟一点时间之类的任务

示例代码
public class Test1 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i+1);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        Test1 test1 = new Test1();
        Thread thread = new Thread(test1);
        thread.start();

    }
}

这个代码就是模拟从1s数到10s的任务,其中关键之处就是Thread.sleep(1000);这里的代码就

3)线程的阻塞–礼让

礼让就是指,有两个线程A\B,此时A在cpu上运行,然后A礼让cpu的位置,然后和B一起重新让cpu进行调度,此时的结果就是A再次上cpu或者B得到礼让然后上cpu进行运行

示例代码
import com.galgame.eriya.pojo.Admin;
import com.galgame.eriya.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.http.HttpResponse;

public class Test1 implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"号线程启动");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"号线程停止");
    }
}

class TestYield{
    public static void main(String[] args) {
        Test1 test1 = new Test1();
        new Thread(test1,"线程1").start();
        new Thread(test1,"线程2").start();
    }
}
线程2号线程启动
线程1号线程启动
线程1号线程停止
线程2号线程停止

这里我们可以看到当2号线程启动后就开始礼让,并礼让成功,然后1号线程上cpu运行并一直运行到结束并未再次礼让,最后当1号结束后2号继续运行

4)线程的阻塞–join

简单来说,就是比如你在做核酸检测的时候明明快要排到你了,但是却来了一群胡搅蛮缠的二流子,因为你打不过他们所以只能等待他们做完后你才能接着做。而转换到线程来说就是,当主线程开始执行后,接收到join的指令后,vip线程就开始上cpu执行而只有等到vip线程执行结束后,主线程才能继续执行

示例代码
import com.galgame.eriya.pojo.Admin;
import com.galgame.eriya.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.http.HttpResponse;

public class Test1 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("啊哈哈哈鸡汤来咯~"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Test1 test1 = new Test1();
        Thread thread = new Thread(test1);
        thread.start();

        for (int i = 0; i < 500; i++) {
            if(i==200){
                thread.join();
            }
            System.out.println("主线程"+i);
        }
    }
}


9.线程的优先级

在生活中,我们做任何事都有一个优先级的顺序,比如我们得先把肚子填饱才能去做其他的事情,所以能吃饱饭是优先级最高的事情,其他的事情就比吃饱饭的优先级要低。而换到程序上来看也是一样的,每个线程都有自己的优先级,优先级的不同代表着执行的顺序不同,而优先级又是从1-10这样排序的,而我i们也可以通过getPriority()和setPriority()来查看一个线程的优先级和修改一个线程的优先级。当然,在我们设置了线程的优先级顺序后cpu也不一定完全按照这个优先级来调度

示例代码
public class Test1 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

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

        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
        Test1 test1 = new Test1();
        Thread thread = new Thread(test1);
        Thread thread1 = new Thread(test1);
        Thread thread2 = new Thread(test1);
        Thread thread3 = new Thread(test1);
        Thread thread4 = new Thread(test1);

        thread.setPriority(4);
        thread.start();

        thread1.setPriority(Thread.MAX_PRIORITY);
        thread1.start();

        thread2.setPriority(6);
        thread2.start();

        thread3.setPriority(2);
        thread3.start();

        thread4.setPriority(Thread.MIN_PRIORITY);
        thread4.start();

    }
}

main-->5
Thread-0-->4
Thread-1-->10
Thread-2-->6
Thread-4-->1
Thread-3-->2

这里我们可以看到主线程一定会在所有线程执行前执行,但是我们之前给线程1设置了最高优先级后任然没有在主线程之后执行,而是由线程0最先执行。由此可见,对于cpu来说线程的优先级更多的是一种参考,真正执行的时候还得看它的心情

10.守护线程

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

2、jvm虚拟机必须等待用户线程结束才能结束

3、jvm虚拟机不要要等待守护线程结束就可以结束

示例代码

public class Test1 implements Runnable {
    @Override
    public void run() {
        while (true){
            System.out.println("守护还活着");
        }
    }
}

class You implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("用户线程活了"+i+"天了");
        }
        System.out.println("用户线程死了");
    }
}

class ThreadStart{
    public static void main(String[] args){
        Test1 test1 = new Test1();
        You you = new You();

        Thread thread = new Thread(test1);
        thread.setDaemon(true);
        thread.start();

        Thread thread1 = new Thread(you);
        thread1.start();
    }
}

这段程序的结果是当用户线程结束后没过多久,守护线程也结束了。按理说,守护线程会永远执行下去,但是当jvm停止后它也就停止了,所以这正印证了jvm虚拟机不要要等待守护线程结束就可以结束


11.线程同步和锁

1)同步方法和方法块

我们知道,在实现runnable接口后的线程是不安全的,就比如写一个买票的程序,一共有10张票,三个人去抢,但是可以知道的是一张票三个人都会看到,所以会导致三个人可能都会拿到同一张票在没有同步的程序里,而更直观的是在银行取钱的业务中,你有着100元,你和你的兄弟都要取出这一百元,然后你们同时登录取钱都会显示账户有100元,当两人同时点击取钱按钮后,在没有同步的线程中,你们兄弟二人都会拿到100元。

所以给需要的方法同步,才能有效解决这样的问题。而给方法加上同步的方法则是 public synchronized void way(){}在返回值前添加synchronized即可,而同步方法块只需要在方法里面添加一个synchronized(){}在里面写要同步的方法即可

示例代码--买票
public class Test1 implements Runnable {
    //设置线程状态标志
    private boolean flag =true;
    //设置票数
    int trick=10;


    @Override
    public void run() {
        while(flag){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            buy();
        }
    }

    //购票方法
    public synchronized void buy(){
        //如果票清空则把线程状态设为结束
        if (trick<=0){
            flag=false;
            return;
        }
        System.out.println(Thread.currentThread().getName()+"买到票");
        trick--;
    }
}

class BuyTrick{
    public static void main(String[] args) {
        Test1 test1 = new Test1();
        Thread thread = new Thread(test1,"线程1号");
        Thread thread1 = new Thread(test1, "线程二号");

        thread.start();
        thread1.start();
    }
}

这里我在buy这个方法前面加了synchronized ,这样就会在某一个人买票的时候锁住票,只有当这个人确确实实的拿到了票。且总票数减少后才会再次开放购买

2)死锁

死锁的本质其实就是一个进程占用了一个资源,但是这个资源一次只能被一个进程使用,而另一个进程也占用着一个资源。最后两个进程都需要对方的资源,却又都锁着对方需要的资源,此时就会发生死锁

示例代码
public class Test1 implements Runnable {
    //设置一个进程一次只能被一个进程所调用
    static Phone phone = new Phone();
    static Cpu cpu = new Cpu();

    int choose;//选择
    String name;//选择的进程

    public Test1(int choose, String name) {
        this.choose = choose;
        this.name = name;
    }

    @Override
    public void run() {
        try {
            use();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //进程使用方法
    public void use()throws InterruptedException{
        if(choose==0){
            //当第一个进程开始
            synchronized (phone){//拿到了phone这个资源并上锁
                System.out.println(Thread.currentThread().getName()+"拿到了电话锁");
                Thread.sleep(1000);

                synchronized (cpu){//在拿到phone后想要继续拿到cpu这个资源
                    System.out.println(Thread.currentThread().getName()+"拿到了cpu锁");
                }
            }
        }else{
            synchronized (cpu){//拿到了cpu这个资源并上锁
                System.out.println(Thread.currentThread().getName()+"拿到了cpu锁");
                Thread.sleep(1000);
                synchronized (phone){//在拿到cpu后想要继续拿到phone这个资源
                    System.out.println(Thread.currentThread().getName()+"拿到了电话锁");
                }
            }
        }
    }


}

class Phone{}
class Cpu{}

class Main{
    public static void main(String[] args) {
        Test1 t1 = new Test1(0, "进程1");
        Test1 t2= new Test1(1, "进程2");

        //启动线程
        new Thread(t1).start();
        new Thread(t2).start();
    }
}

Thread-0拿到了电话锁
Thread-1拿到了cpu锁

当执行这段程序时,我们会发现一直卡在Thread-0拿到了电话锁 Thread-1拿到了cpu锁 这里然后就没有动静了,但是程序并没有结束此时就发生了死锁

想要结束死锁,只有破除一下条件的任意一种即可

1、互斥条件:一个资源一次只能被一个进程所调用

2、请求与保持条件:一个进程在请求资源阻塞的时候,对其所持有的资源不会释放

3、不剥夺条件:在进程使用完资源前不会强行剥夺资源

4、若干进程形成头尾相接的循环等待资源的关系

而上面代码想要解除死锁状态很简单,只需要把嵌套顺序改变即可

示例代码
//当第一个进程开始
            synchronized (phone){//拿到了phone这个资源并上锁
                System.out.println(Thread.currentThread().getName()+"拿到了电话锁");
                Thread.sleep(1000);
            }
            synchronized (cpu){//在拿到phone后想要继续拿到cpu这个资源
                System.out.println(Thread.currentThread().getName()+"拿到了cpu锁");
            }

3)lock锁

相比于synchronized方法来说

1、lock是显示锁,没有关闭不会自动关闭,synchronized是隐式锁出了作用域后自动关闭

2、lock只能锁代码块不能锁方法,而synchronized是可以锁方法的

3、lock更高效

示例代码-之前的购票系统的lock版
public class Test1 implements Runnable {
    //
    private final ReentrantLock lock = new ReentrantLock();
    //设置线程状态标志
    private boolean flag =true;
    //设置票数
    int trick=10;


    @Override
    public void run() {
        while(flag){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            buy();
        }
    }

    //购票方法
    public void buy(){
        lock.lock();
        try {
            //如果票清空则把线程状态设为结束
            if (trick<=0){
                flag=false;
                return;
            }
            System.out.println(Thread.currentThread().getName()+"买到票");
            trick--;
        }finally {
            lock.unlock();
        }

    }
}

class BuyTrick{
    public static void main(String[] args) {
        Test1 test1 = new Test1();
        Thread thread = new Thread(test1,"线程1号");
        Thread thread1 = new Thread(test1, "线程二号");

        thread.start();
        thread1.start();
    }
}

12.管程法

本质就是生产者和消费者的问题,进程在原则上是互不干扰的,偶尔是能够在一个进程中设置flag然后停止一个进程。而要两个进程之间又关联就需要一个缓存区,这里就拿鸡的生产和消费来举例子

示例代码
public class Test1 {
    public static void main(String[] args) {
        Buffered buffer = new Buffered();
        //生产者开始
        new Producer(buffer).start();
        //消费者开始
        new Consumer(buffer).start();
    }
}

//生产者类
class Producer extends Thread{
    Buffered buffer;

    public Producer(Buffered buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        //开始生产
        for (int i = 0; i <100 ; i++) {
            try {
                buffer.push(new Chicken(i));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("生产了"+i+"只鸡");
        }
    }
}

//消费者类
class Consumer extends Thread{
    Buffered buffer;

    public Consumer(Buffered buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        //开始消费
        for (int i = 0; i <100 ; i++) {
            try {
                buffer.buy();
                System.out.println("消费了"+buffer.buy().id+"只鸡");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//鸡类
class Chicken{
    int id;

    public Chicken(int id) {
        this.id = id;
    }
}

//创建缓存区
class Buffered{
    //需要大小
    Chicken[] chickens = new Chicken[10];

    //计数器
    int count=0;
    //需要生产者放入产品
    public synchronized void push (Chicken chicken)throws InterruptedException{
        //如果容器满了,通知消费
     while (count==chickens.length){
         this.wait();
     }
     //如果容器没满,把鸡放进去
     chickens[count]=chicken;
     count++;
     this.notifyAll();
    }

    public synchronized Chicken buy() throws InterruptedException{
        //判断能否消费
        while(count==0){
            System.out.println("不能消费");
            this.wait();
        }

        //缓存区不为0则能消费
        count--;
        Chicken chicken =chickens[count];
        this.notifyAll();
        //吃完了通知在生产
        return chicken;
    }

}

13.线程池

1)线程池的好处

1、把线程都放入一个线程池中后,不需要再频繁的创建和销毁,能够实现用完后再放回

2、效率高

3、降低了资源消耗,并且便于线程的管理

2)线程池的创建

要使用线程池就需要创建ExecutorService接口并使用Executors类中的newFixedThreadPool方法

示例代码
public class Test1 {
    public static void main(String[] args) throws InterruptedException{
        MyThread myThread = new MyThread();
        ExecutorService service = Executors.newFixedThreadPool(10);
        service.execute(myThread);
        service.execute(myThread);
        service.execute(myThread);


        Thread.sleep(3000);
        service.shutdown();

        System.out.println("是否已经关闭"+service.isShutdown());

    }
}

class MyThread implements Runnable{

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"号进程");
    }
}

pool-1-thread-1号进程
pool-1-thread-3号进程
pool-1-thread-2号进程
是否已经关闭true

总结

这估计是我Java篇的最后的文章了,不是不想深入,在校的时候已经自学到微服务了。但是找工作很是碰壁,无奈之下只能转运维了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值