多线程


多线程

线程和进程

进程

一个进程是一个正在运行的程序,进程是操作系统资源分配(计算资源,比如CPU,存储)的最小单位

电脑同时运行多个程序,一个时间段上在多个极短的时间片争抢运行,就是争抢内存,而在我们看来就是多个进程同时运行

线程

一个时间片又被分为多个片段,分给每一个线程来执行,而这个片段就是线程,比进程还要小的单位,而这个时间片存在不同的线程,同时这个时间片的时间很短,所以看起来不同的线程操作同时发生

java的main函数就是一个主线程,而在主线程中开启子线程,实现线程的并发

多线程

如果一个进程拥有不止一个线程,这样的程序称为多线程程序

java多线程的实现方式

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
  3. 通过Callable和FutureTask创建线程
  4. 通过线程池创建线程

继承Thread类

1、创建一个类,使其继承Thread

2、重写run方法

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("接收信息++");
        }
    }
}
public class MyThread01 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("发送信息--");
        }
    }
public class Test {
    public static void main(String[] args) {
        MyThread01 myThread01 = new MyThread01();
        MyThread myThread = new MyThread();
        myThread01.start();//开始启用线程,发送信息
        myThread.start();//开始启用线程,接受信息
    }
}

image-20220623201349221

  • 主线程可以开启多个子线程,但是主线程中的代码不影响执行

  • 只有主线程的代码执行完,才会执行main方法下面的代码

  • 两个线程随机抢占资源,而cpu执行的效率过快,不会出现交替(线程一和线程二执行的输出内容的交替),所以得加大实例

  • 如果使用run方法,则不会按照线程来执行,和之前调用方法一样

实现Runnable接口

1、创建一个类实现Runnable接口

2、重写run方法

public class MyThread implements Runnable{

    private String msg;
    public MyThread(String msg){
        this.msg = msg;
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(msg);
        }
    }
}
public class Test {
    public static void main(String[] args) {
        MyThread myThread = new MyThread("发送信息");
        MyThread myThread1 = new MyThread("接受信息");
        Thread thread = new Thread(myThread);//借助Thread
        thread.start();
        Thread thread1 = new Thread(myThread1);
        thread1.start();

    }
}

通过Thread构造方法将Runnable传入

image-20220623205417834

输出:

image-20220623205546901

这种方法较为复杂,通常会使用继承Thread类,可以对其进行简化

public class MyThread implements Runnable{

    private String msg;
    public MyThread(String msg){
        this.msg = msg;
    }

    public MyThread() {
    }

    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(Thread.currentThread().getName());//使用方法将Thread名称获得
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Runnable my = new MyThread();
        Thread thread = new Thread(my,"发送信息");
        thread.start();
        Thread thread1 = new Thread(my,"接受信息");
        thread1.start();
    }
}

但是这种方法也有局限,Runable只能实现多线程业务一样,run方法中的代码一样

通过Callable和FutureTask创建线程

在java1.5之前,创建线程的方法只有两种,一种是继承Thread,另一种是实现Runnable接口,同时局限是线程的run方法没有返回值,如若需要需要通过共享变量和使用线程通信的方法来达到效果

java1.5开始提供Callable和Future,通过他们可以在任务结束后得到任务执行结果

  • Callable

1、实现Callable接口,重写带有返回值的call方法(返回值可以加泛型 )

2、使用FutureTask封装成一个任务

public class MyThread implements Callable<String> {

    public String call() throws Exception {
        for (int i = 0; i < 1000; i++) {
            System.out.println("接收信息");
        }
        return "over";
    }
}
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread myThread = new MyThread();
        FutureTask futureTask = new FutureTask(myThread);
        Thread thread = new Thread(futureTask);
        thread.start();
        Object o = futureTask.get();
        System.out.println(o);
    }
}

输出:

image-20220623213519211

而FutureTask的get方法有阻塞作用,启动线程后,获取get方法的返回值,则只有执行完子线程的代码才能执行主线程的代码

线程池创建线程

1、创建Callable实现类,并重写call方法

2、建立线程池

3、通过Callable和FutureTask创建线程

4、关闭线程池

public class MyThread implements Callable<String> {

    public String call() throws Exception {
        for (int i = 0; i < 1000; i++) {
            System.out.println("接收信息");
        }
        return "over";
    }
}
public class MyThread1 implements Callable<String> {

    public String call() throws Exception {
        for (int i = 0; i < 1000; i++) {
            System.out.println("发送信息");
        }
        return "over";
    }
}
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        MyThread myThread = new MyThread();
        MyThread01 myThread1 = new MyThread01();
        Future future = executorService.submit(myThread);
        Future future1 = executorService.submit(myThread1);
        Object o = future.get();
        System.out.println(o);
        Object o1 = future1.get();
        System.out.println(o1);
    }
}

image-20220624194101883

程序并没有结束,因为线程池并没有结束,可以通过ExecutorService的shutdownshutdownNow关闭

线程状态及周期

常用方法

  • currentThread():获取当前线程对象
  • setName():设置线程名称
  • getName():获取线程名称
  • setPriority(int priority):设置线程的优先级。优先级的范围1-10,默认是5
  • getState():获取线程的状态
  • join():执行线程会阻塞该线程
  • sleep():指定休眠时间,单位是毫秒
  • start():启动线程,并不一定会执行run方法
  • yield():暂定该线程的执行,交出cpu的使用权

线程状态

线程的状态有四种:初始状态、可运行状态、运行状态和终止状态

image-20220625162958645

初始状态:创建了一个线程对象,并不等于在系统中创建了一个新线程。一旦调用完了start方法线程就进入了可运行状态不会回到初始状态,因此在线程对象的整个生命周期中,只能调用一次start方法。如果对线程对象调用了多次start方法,就会出现一个IllegalStateException异常

可运行状态:线程为运行做好了准备,只等获得cpu来运行

运行状态:只能有一个线程处于运行状态

终止状态:当一个线程执行完run方法中的代码,该线程就会进入终止状态

阻塞状态:如果一个线程要进入运行状态,就要获得cpu时间片,但是某种情况下,线程运行不仅需要cpu进行运算,还需要一些别的条件,例如用户输入等待、网络传输,在这种情况下,即使获得cpu时间片,也不能运行,也因此为了不让这些进程占用cpu的时间,会让这些线程进入阻塞状态

同时除了I/O会进入阻塞状态之外,sleep方法也会进入阻塞状态,可以让线程进入“睡眠“状态

还有join方法,添加等待的时间(毫秒)后,就会从阻塞状态转化为可运行状态

image-20220625165138377

public class MyThread1 extends Thread{

    Thread t;

    public void run() {
        try {
            t.join(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 100; i++) {
            System.out.println(i+"$$$");
        }
    }
}
public class MyThread2 extends Thread{
    Thread t;

    public void run() {
        try {
            t.join(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 100; i++) {
            System.out.println(i+"###");
        }
    }
}
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyThread1 myThread1 = new MyThread1();
        MyThread2 myThread2 = new MyThread2();
        myThread1.t = myThread2;
        myThread2.t = myThread1;
        myThread1.start();
        myThread2.start();
    }
}

如果join方法不添加时间的话,无法运行。myThread1的线程一直在等待myThread2的线程结束,而myThread2的线程一直在等待myThread1的线程结束,因此两边的线程都处于阻塞状态而无法运行

线程安全问题

当多个线程同时共享同一个全局变量或静态变量,做写操作时,可能会发生数据冲突问题,也就是线程安全问题,但是读操作不会发生数据冲突问题

解决:在线程执行的过程中 ,只允许一个线程进入,修改完变量后,再允许一个线程,将多线程改变为单线程,这样就解决了线程安全问题

案例:用两个线程模拟两个窗口,对车票进行写操作

public class ThreadDemo {
    public static void main(String[] args) {
        //同时共享同一变量
        ThreadTrain threadTrain = new ThreadTrain();
        Thread t1 = new Thread(threadTrain,"窗口1");
        Thread t2 = new Thread(threadTrain,"窗口2");
        t1.start();
        t2.start();
    }
}
class ThreadTrain implements Runnable{
    private int trainCount = 100;
    @Override
    public void run() {
        while (trainCount>0){
            try {
                Thread.sleep(50);//线程进入阻塞状态 ,其他线程可以获取时间片
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //出售火车票
            sale();
        }
    }

    private void sale() {
        System.out.println(Thread.currentThread().getName()+"出售第"+(100-trainCount+1)+"张票");
        trainCount--;
    }
}

出现一下问题,窗口1和2卖出同一张票,并且原本就100张票,结果卖到了101张,这就是线程安全问题

image-20220629111553448

通过关键字synchronized给操作上锁

    private synchronized void sale() {
        if (trainCount>0){
            System.out.println(Thread.currentThread().getName()+"出售第"+(100-trainCount+1)+"张票");
            trainCount--;
        }
    }

image-20220629112836032

线程同步

并行指的是线程同时执行

同步并不是线程同时执行,而是线程不同时执行。同步的本质指的是数据的同步。一般情况下,线程之间是相互独立的,如果都去访问同一个变量,极有可能这个变量会变乱。如果不想数据变乱,应该不让他们同时访问同一个数据变量,这个控制的过程称为线程同步

Synchronized关键字

java中采用锁来保护临界资源,防止数据不一致的情况发生

在java中,每一个对象都有一个互斥锁标记,可以用来分给不同的线程。之所以这个锁标记是互斥的,因为这个锁标记只能分配给一个线程,同时还要有关键字Synchronized进行加锁的操作

用法

  • Synchronized+代码块

    Synchronized(obj){
        
    }
    

    如果某一个线程想要执行代码块中的代码,就必须先获得obj所指对象的互斥锁标记,也就是说如果有一个线程t1想要进入同步代码块,就必须获得obj对象的标记;而如果t1线程正在同步代码块中运行,说明t1有着obj的互斥锁标记,而这时如果有一个线程t2想要访问同步代码块,会因为拿不到obj对象的锁标记而无法执行同步代码块中的代码块

    obj这个可以是任何一个对象,可以是this

  • 同步方法

    通过关键字synchronized给方法上锁实现线程同步

    public class ThreadDemo {
        public static void main(String[] args) {
            //同时共享同一变量
            ThreadTrain threadTrain = new ThreadTrain();
    //        SellWindow sellWindow = new SellWindow();
    //        Thread t1 = new Thread(sellWindow,"窗口1");
    //        Thread t2 = new Thread(sellWindow,"窗口2");
            Thread t1 = new Thread(threadTrain,"窗口1");
            Thread t2 = new Thread(threadTrain,"窗口2");
            t1.start();
            t2.start();
    
        }
    }
    class ThreadTrain implements Runnable{
        private int trainCount = 150;
    
        @Override
        public void run() {
            while (trainCount>0){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //出售火车票
                sale();
            }
        }
    
        private synchronized void sale() {
            if (trainCount > 0) {
                System.out.println(Thread.currentThread().getName() + "出售第" + (150 - trainCount + 1) + "张票");
                trainCount--;
            }
        }
    }
    
  • 使用锁对象上锁和解锁

    public class ThreadDemo {
        public static void main(String[] args) {
            //同时共享同一变量
            SellWindow sellWindow = new SellWindow();
            Thread t1 = new Thread(sellWindow,"窗口1");
            Thread t2 = new Thread(sellWindow,"窗口2");
            t1.start();
            t2.start();
    
        }
    }
    class SellWindow implements Runnable{
        private int tickets = 100;
        Lock lock = new ReentrantLock();
        @Override
        public void run() {
            while (tickets>0){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String threadName = Thread.currentThread().getName();
                lock.lock();
                tickets--;
                if (tickets>=0){
                    System.out.println(threadName+"卖掉1张票,剩余"+tickets);
                }
                lock.unlock();
            }
        }
    }
    

    image-20220630112110157

Synchronized原理

jdk1.4之前直接上重量级锁,现在分为三种

  • 偏向锁:只有一个线程,不用争抢时间片,只是加上了一个轻量化锁,只是一个标记
  • 轻量级锁:两个线程,当其中一个线程sleep时,另一个线程执行,依次来回交替执行
  • 重量级锁:适用于线程比较多和执行同步代码时间比较长,原理很简单,直接让线程挂起

线程通信

在synchronized关键字的作用下可能会产生新的问题:死锁

a对象的锁标记被t1线程所获得,而b对象的锁标记被t2线程所获得,因此对于t1线程无法获得b对象的锁标记,会进入b对象的锁池,等待b对象锁标记的释放,而对于t2线程无法获得a对象的锁标记,会计入a对象的锁池,等待a对象锁标记的释放

public class MyThread3 extends Thread{
    private Object object1;
    private Object object2;

    public MyThread3(Object object1, Object object2) {
        this.object1 = object1;
        this.object2 = object2;
    }

    @Override
    public void run() {
        synchronized (object1){
            System.out.println("线程一进入第一个锁");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (object2){
                System.out.println("线程二进入第二锁");
            }
        }

    }
}
public class MyThread4 extends Thread{
    private Object object1;
    private Object object2;

    public MyThread4(Object object1, Object object2) {
        this.object1 = object1;
        this.object2 = object2;
    }
    @Override
    public void run() {
        synchronized (object2){
            System.out.println("线程一进入第一个锁");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (object1){
                System.out.println("线程二进入第二锁");
            }
        }
    }
}
public class Test3 {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();

        MyThread3 myThread3 = new MyThread3(o1,o2);
        MyThread4 myThread4 = new MyThread4(o1,o2);

        myThread3.run();
        myThread4.run();
    }
}

出现死锁,对于线程一进入不了第二个锁,线程一没有获得object2对象的锁标记,因为线程二并没有释放object2对象的锁标记,对于线程二进入不了第二个锁,线程二没有获得object1对象的锁标记,因为线程二并没有释放object2对象的锁标记

image-20220701173620297

如何解决死锁问题

在java中使用wait和notify这两个方法,来解决死锁问题

wait()表明让线程暂时释放对象的锁标记,而notify()就是唤醒处于wait状态的线程,而且必须是同一个监视器下的线程

@Override
    public void run() {
        synchronized (object1){
            System.out.println("线程一进入第一个锁");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                object1.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (object2){
                System.out.println("线程一进入第二个锁");
            }
        }

    }
@Override
    public void run() {
        synchronized (object2){
            System.out.println("线程二进入第一个锁");
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (object1){
                System.out.println("线程二进入第二个锁");
                object1.notify();
            }
        }
    }

线程一调用object1的wait方法,线程一暂时释放自己拥有的object1对象的锁标记,而进入另一个线程,而因为object1对象的锁标记暂时释放和线程二拥有object2对象的锁标记,因此线程一的剩下代码无法执行,而执行线程二的剩下代码,执行完毕后,线程二释放object2对象的锁标记,因此线程一获得object2对象的锁标记,而执行剩下的代码

image-20220701180600231

image-20220701192039163

可能有多个线程调用对象的wait方法,因此在对象等待状态中的线程可能有多个,而notify方法,会从对象等待状态中的线程里挑选一个线程进行唤醒。而notifyAll方法会把等待状态的所有线程都唤醒

public class MyThread5 implements Runnable{
    @Override
    public synchronized void run() {
        for (int i = 1; i <= 10; i++) {
            if (i==3){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            notify();
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
public class Test5 {
    public static void main(String[] args) {
        MyThread5 myThread5 = new MyThread5();
        Thread thread1 = new Thread(myThread5,"aaa");
        Thread thread2 = new Thread(myThread5,"bbb");
        thread1.start();
        thread2.start();
    }
}

image-20220701205143037

aaa线程执行wait方法,而去执行bbb线程就会执行到notify方法,也属于其他线程调用了notify方法,aaa线程获得释放立即执行,这种想法错误的,因为两个线程同步,只有bbb线程执行到wait方法进入等待状态时,aaa线程才会继续执行,但是也会执行到notify方法,但是bbb线程并不会释放执行,只有等到aaa线程执行代码才会执行bbb线程的代码

说明:wait方法和notify方法必须在线程同步的情况下使用,因此必须在同一个监视器执行,得使用synchronized关键字上锁

生产者-消费者模式

有商品时,消费者才可以消费,没有商品时,消费者等待。商品库存充足时,生产者等待,消费者消费商品

public class Saler {
    private int productCount=10;

    /**
     * 生产商品
     */
    public synchronized void stockGoods(){
        if (productCount<20){
            productCount++;
            System.out.println(Thread.currentThread().getName()+"生产了一件商品,库存是"+productCount);
            this.notify();
        }else {
            System.out.println("库存满了");
            try {
                this.wait();//库存满了,停下生产等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 消费商品
     */
    public synchronized void sellGoods(){
        if (productCount>0){
            productCount--;
            System.out.println(Thread.currentThread().getName()+"购买了一件商品,库存剩余"+productCount);
            this.notify();
        }else {
            System.out.println("库存不足");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Customer implements Runnable{
    private Saler saler;

    public Customer(Saler saler) {
        this.saler = saler;
    }

    @Override
    public synchronized void run() {
        while (true){
            saler.sellGoods();
        }
    }
}
public class Producer implements Runnable{
    private Saler saler;

    public Producer(Saler saler) {
        this.saler = saler;
    }

    @Override
    public void run() {
        while (true){
            saler.stockGoods();
        }
    }
}
public class Test {
    public static void main(String[] args) {
        Saler saler = new Saler();
        Customer customer = new Customer(saler);
        Producer producer = new Producer(saler);
        Thread thread1 = new Thread(customer,"客户");
        Thread thread2 = new Thread(producer,"厂商");
        thread1.start();
        thread2.start();
    }
}

线程池

java中的线程池是一个管理线程的池子,可以在需要时开辟线程,可以控制最大开辟的线程个数,可以在不需要线程的时候关闭线程,可以让任务执行。这些管理过程不要我们干预,线程池也能帮我们完成,只需要添加任务

为什么要有线程池

多线程解决了任务并发问题,但是开辟和关闭线程很消耗系统性能,而且开辟和关闭线程要处理很多细节,频繁的开辟和关闭线程会给系统增加很多开销,线程池使用重用的概念,可以控制线程开辟的数量,复用这些线程执行任务,不用频繁的开辟和关闭线程

线程池使用场景

线程池适合执行时间短,工作内容较为单一的任务

好处:

  • 降低资源消耗:重复利用已创建的线程降低线程创建和销毁造成的开销
  • 提高反应速度:当任务达到时,不用等待线程创建就能立即执行
  • 提高线程的可管理性:统一对线程进行分配、调优和监控
  • 提供更多的功能:具备可拓展性,允许开发人员向其中增加更多的功能。延时定时线程池SchedulerThreadPoolExecutor
  • 充分利用cpu、内存、网络、io等系统资源。线程的创建需要开辟虚拟机栈、本地方法栈、程序计数器等线程私有的内存空间
  • 服务器负载过大时,线程池协调多个线程,并实现主次线程隔离、定时执行、周期执行等任务

作用:

  • 管理线程并复用线程、控制最大并发数等
  • 实现任务线程队列缓存策略和拒绝机制
  • 实现和时间相关的功能,定时执行、周期执行
  • 隔离线程环境。通过配置两个或者多个线程池,将一台服务器上较慢的服务和其他服务隔离开,避免各服务线程相互影响

shutdownNow和shutdown的区别

  • shutdown():当线程池调用该方法时,线程池的状态立刻变成SHUTDOWN状态,不能在往线程池中添加任何任务,否则会抛出RejectedExecutionException异常,但是线程并不会立即退出,而是将线程池的任务都处理完毕后,才会退出
  • shutdownNow():当线程池调用该方法时,线程池的状态立刻变成STOP状态,并停止所有正在执行的线程,不在处理还处于队列中等待的任务,并返回等待执行任务的列表

线程池工具类

Exectors是线程工具类,可以快速构建线程池

三种常见的线程池:

  • 固定线程个数的线程池
  • 不限线程个数的线程池(起始容量为0,最大容量为2的32次方-1)
  • 单个线程的线程池,也是串行任务池
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(Thread.currentThread().getName()+"----"+i);
        }
    }
}
public class PoolTest {
    public static void main(String[] args) {
        //固定线程个数
//        ExecutorService es = Executors.newFixedThreadPool(3);
        //单个线程的线程池
//        ExecutorService es = Executors.newSingleThreadExecutor();
        //不限线程个数的线程池
        ExecutorService es = Executors.newCachedThreadPool();
        es.submit(new MyRunnable());
        es.submit(new MyRunnable());
        es.submit(new MyRunnable());
        es.shutdown();
    }
}
  • 带有定时的线程池(必须带有固定线程的参数)
public class Test {
    public static void main(String[] args) {
        //带有定时的线程池
        ScheduledExecutorService se = Executors.newScheduledThreadPool(3);
        se.schedule(new MyRunnable(),1, TimeUnit.SECONDS);
        se.schedule(new MyRunnable(),3, TimeUnit.SECONDS);
        se.shutdown();
    }
}
public class Test {
    public static void main(String[] args) {
        ScheduledExecutorService se = Executors.newScheduledThreadPool(3);
        se.scheduleAtFixedRate(new MyRunnable(),1,2,TimeUnit.SECONDS);
    }
}

1说明的是初次执行线程延时1秒,2说明的是再次执行线程延时2秒

ThreadPoolExecutor

image-20220702222816621

主要参数:

  • corePoolSize:线程池核心线程的大小,表示常驻核心线程数,如果大于0,即使执行完任务,线程也不会被销毁。如果设置过小的话会导致线程频繁的创建和销毁,设置过大会造成资源浪费
  • maximumPoolSize:线程池能够容纳的最大线程数。必须大于等于1
  • keepAliveTime:空闲线程的存活时间,在默认情况下,当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,达到空闲时间的线程会被销毁,直到剩下corePoolSize个线程为止,但是当threadPoolExecutor的allowCoreThreadTimeOut设置为false,核心线程超时也会被回收
  • BlockingQueue:用来暂时保存任务的工作队列
  • RejectedExecutionHandler:线程池已经关闭或者饱和,达到了最大线程数且工作队列已满,executor()方法会调用handler
  • threadFactory:表示线程工厂。它用来生产- -组相同任务的线程。 线程池的命名是通过给threadFactory增加组名前缀来实现的。在用jstack分析时, 就可以知道线程任务是由哪个线程工厂产生的。
  • handler:表示执行拒绝策略的对象。当超过workQueue的缓存 上限的时候,就可以通过该策略处理请求,
    这是一种简单的限流保护。
    • 提供了四个预定义的处理程序策略:
    • 在默认ThreadPoolExecutor。Abortpolicy,处理程序会引发运行RejectedExecut ionException后排斥反应。
    • 在ThreadPoolExecutor . callerRunsPolicy.中,调用execute本身的线程运行任务。这提供了一个简单的反馈控制机制,将降低新任务提交的速度。
    • 在ThreadPoolExecutor DiscardPolicy虫 ,简单地删除无法执行的任务。
    • 在ThreadPoo1Executor . DiscardoldestPolicy虫,如果执行程序没有关闭,则工作队列头部
      的任务被删除,然后重试执行(可能会再次失败,导致重复)。

execute方法执行任务过程

首先,所有的任务都是由execute方法完成的,这部分完成的工作是:检查现在线程池的运行状
态、运行线程数、运行策略,决定接下来的执行流程,是直接申请线程执行,或是缓冲到队列中执行,
亦或是直接拒绝该任务。
其执行过程如下:
1、首先检测线程池运行状态,如果不是RUNNING,则直接拒绝,线程池要保证在RUNNING的状
态下执行任务。
2、如果workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务。
3、如果workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列
中。
4、如果workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻
塞队列已满,则创建并启动一个线程来执行新提交的任务。
5、如果workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满,则根据拒绝策略来
处理该任务,默认的处理方式是直接抛异常。
6、线程池中存活的线程执行完当前任务后,会在循环中反复从BlockingQueue队列中获取任务来
执行

自定义线程

public class Test {
    public static void main(String[] args) {
        ThreadPoolExecutor poo1 = new ThreadPoo1Executor(10202,TimeUnit.SECONDS,new ArrayB1ockingQueue<>(3),newThreadPoolExecutor.DiscardPolicy());
    //poo1. allowCoreThreadTimeout(false);
    poo1. submit(new Runnab1e() {
        @override
        pub1ic void run() {
            System. out. print1n("我是一个任务"+Thread. currentThread() . getName()); 
        }
    });
    for(inti=0;i<200;i++){
        poo1. execute (new Runnab7e() {
            @override
            pub1ic void run() {
                System. out. print1n("我是另外一个任务"+Thread. currentThread().getName());
            });
        }
        try {
            Thread. sleep(4000);
        } catch (InterruptedException e) {
            e. pri ntStackTrace();
        }
        system.out.println(pool.getPoolSize())
    }
}

Author: baiwenhui
Reprint policy: All articles in this blog are used except for special statements CC BY 4.0 reprint policy. If reproduced, please indicate source baiwenhui !
  TOC