JUC高并发编程三

4 篇文章 0 订阅
4 篇文章 0 订阅

7、Callable接口

1、创建线程的多种方式

创建方式备注
集成Thread类无返回值
实现Runnable接口无返回值
通过Callable接口可以有返回值
通过线程池的方式

2、Callable和Runnable接口区别

区别CallableRunnable
是否有返回值
是否抛出异常
方法实现不同call方法run 方法

代码实现:

package com.codetip.codejuc.juc.callable;

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

// 实现Runnable接口
class Mythread1 implements Runnable {
    @Override
    public void run() {

    }
}

// 实现Callable接口
class Mythread2 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return 200;
    }
}

public class CallOrRunDemo1 {

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        // Runnable接口创建线程
        // new Thread(new Mythread1(), "AA").start();

        // 直接使用 Callable,报错
        // new Thread(new Mythread2(), "AA").start();

        // 可以找一个既和Runnable有关有何Callable有关的类
        // Runnable接口有实现类 FutrueTask
        // FctrueTask构造又可以传递Callable

        // FutureTask
        // FutureTask<Integer> f1 = new FutureTask<>(new Mythread2());
        // new Thread(f1, "AA").start();

        // lambda表达式写法
        FutureTask<Integer> f2 = new FutureTask<>(() -> {
            System.out.println(Thread.currentThread().getName() + "  come in Callable");
            return 1024;
        });

        // 创建一个线程
        new Thread(f2, "jack").start();

        // 可以isDone 方法判断是否已经完成
        // while (!f2.isDone()) {
        // System.out.println("Wait...............");
        // }
        // 调用FutureTask的get方法
        System.out.println(f2.get());

        // 再次调用执行结果直接返回
        System.out.println(f2.get());

        // 主线程
        System.out.println(Thread.currentThread().getName() + " come Over");


        /**
         * FutureTask 原理
         * 1、在不影响主线程的任务,单开一个线程去执行其他线程,不影响主线程任务
         * 例如:1、可以统一汇总任务
         *    有四个同学:1计算 1+2+3  2计算:10+11+12.。。50  3计算:60+61  4计算:100+200
         *    第二个同学计算量比较大,
         *    FutureTask 单独开启一个线程给2同学计算,先汇总1,3,4 同学的,最后等2计算出完成,统一汇总
         * 例如、考试的情况,先做会的,在做不会的
         *
         * 最总汇总:只汇总一次
         */
    }
}

8、JUC强大的辅助类

1、CountDownLatch 减少计数

  • 概述

    • CountDownLatch类是可以设置一个计数器,然后通过CountDown方法进行减1 操作,使用await方法方法等待计数器不大于0 ,然后继续执行await方法之后的语句。
    • CountDownLatch主要有两个方法,当一个或多个线程调用await方法是,这些线程会阻塞
    • 其他线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞)
    • 当计数器的值变为0时,因await方法阻塞的线程会被唤醒,继续执行。

举例:6个同学全部走出教室后,班长才能锁门

// 不是用CountDownLatch
package com.codetip.codejuc.juc.callable;

// 演示 CountDownLatch
public class CountDownLatchDemo {
    // (不适用CountDownLatch) 6个同学陆续离开教室后,班长才能锁门
    public static void main(String[] args) {
        // 6个同学陆续离开教室
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "  同学离开教室");
            }, i + "同学").start();
        }
        System.out.println(Thread.currentThread().getName() + "班长开始锁门走人了");
    }
}

// 使用CountDownLatch 
package com.codetip.codejuc.juc.callable;
import java.util.concurrent.CountDownLatch;
// 演示 CountDownLatch
public class CountDownLatchDemo {
    // (不适用CountDownLatch) 6个同学陆续离开教室后,班长才能锁门
    public static void main(String[] args) throws InterruptedException {
        // 设置计数器 为 6
        CountDownLatch count = new CountDownLatch(6);
        // 6个同学陆续离开教室
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "  同学离开教室");
                // 计数减1
                count.countDown();
            }, i + "同学").start();
        }
        count.await();
        System.out.println(Thread.currentThread().getName() + "班长开始锁门走人了");
    }
}

2、CyclicBarrier循环栅栏

  • 概述

    • 一个同步的辅助类,它允许一组线程互相等待,直到达到设定的一个公共屏障点,在涉及一组固定大小的线程的程序中,这些线程必须不定时的互相等待,此时CyclicBarrier很有用。因为该Barrier在释放等待线程后可以重用,所以它称为循环的Barrier。

      举例:七龙珠中:必须集齐七颗龙珠才能召唤神龙。

  • 代码实现:

    package com.codetip.codejuc.juc.callable;
    
    import java.util.concurrent.CyclicBarrier;
    
    // 集齐7颗龙珠召唤神龙
    public class CyclicBarrierDemo {
        // 设置固定值
        private static final int num = 7;
    
        public static void main(String[] args) {
            // 创建 CyclicBarrier
            CyclicBarrier cyclikBarrier = new CyclicBarrier(num, () -> {
                System.out.println("哈哈****,集齐七颗龙珠就可以召唤神龙了!!!!");
            });
    
            // 集齐七颗龙珠的过程
            for (int i = 1; i <= 7; i++) {
                new Thread(() -> {
                    try {
                        System.out.println(Thread.currentThread().getName() + " 星,龙珠被收集到了!");
                        等待
                        cyclikBarrier.await();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }, String.valueOf(i)).start();
            }
        }
    }
    
    

    运行截图

      

 

3、Semaphore信号灯

  • 概念

    • 一个计数的信号量,从概念上来讲,信号量维护了一个许可集。如果有必要,在需要许可可用之前会阻塞每一个acquire(),然后在获取该许可,每个release()就是添加(释放)一个许可,杏儿可能释放一个正在阻塞的获取者,但是不使用时机的许可对象,Semapshore只对可用许可的号码进行计数,并蔡旭相应的行动。
  • 举例:停车场。来一辆车就会获取一个许可(acquire),车满了,停车场不能在进入车辆了,其他必须等待。当有车辆离开,释放一个车位(release),其他车辆就可以继续获取(acquire)停车了。

代码案例:

package com.codetip.codejuc.juc.callable;

import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
// 6辆汽车,3个停车位
public class SemapHore {
    // 定义3个车位
    public static final int semaphoreNum = 3;
    public static void main(String[] args) {
        // 创建 semaphore ,设置许可数量
        Semaphore semaphore = new Semaphore(semaphoreNum);
        for (int i = 1; i <= 6; i++) {
            new Thread(() -> {
                try {
                    // 随机停留时间
                    int wait = new Random().nextInt(5);
                    // 获取一个许可
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName() + " 号车,抢到了车位!!");
                    TimeUnit.SECONDS.sleep(wait);
                    System.out.println(Thread.currentThread().getName() + " -----号车,离开了车位!!,停留了:" + wait + "秒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 释放
                    semaphore.release();
                }
            }, String.valueOf(i)).start();
        }
    }
}

运行结果:

 

9、ReentrantReadWriteLock读写锁

1、悲观锁

  • 修改数据时,认为别人都可能会修改,所以先进行上锁,修改完成后在解锁。

    • 缺点:不支持并发,效率低

2、乐观锁

  • 修改数据时,认为别人不会修改数据,得到数据的时候会有一个版本号,修改数据时会根据这个版本号需求改,如果版本号不一致,则别人修改过了。

    • 支持并发,

3、读写锁

  • 读锁:共享锁
  • 写锁:独享锁

两者都会发送死锁:

读锁:有两个读操作,读1和读2, 线程在读的时候也可以做其他操作。读1 :进行了读的操作和写操作,读2:也正在读。读1 要等到读2 读完才能修改。这时读2也进行了修改操作,这时候就会出现死锁情况。读1 在等读2完成后才能进行修改,读2 在等读1完成后才能操作。

写锁:线程1:对第一条记录进行一次写操作。线程2对第二条记录进行了一次写操作。线程1又对第二条记录进行修改操作。同时线程2又对第一条进行修改操作。这个时候就会发送死锁,线程1 和线程 2 互相等待释放锁。

4、代码演示:

  1. 不加读写锁
package com.codetip.codejuc.juc.readOrWrite;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

// 资源类
class MyCahe {
    // 创建Map集合
    private volatile Map<String, Object> map = new HashMap<>();

    // 存数据
    public void put(String key, Object value) {
        System.out.println(Thread.currentThread().getName() + " 正在写+++,Key为:" + key);
        try {
            TimeUnit.MICROSECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 放数据
        map.put(key, value);
        System.out.println(Thread.currentThread().getName() + " 写+++执行完成,Key为:" + key);
    }

    // 读取数据
    public Object get(String key) {
        Object result = null;
        System.out.println(Thread.currentThread().getName() + " 正在读取---,Key为:" + key);
        try {
            TimeUnit.MICROSECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 放数据
        result = map.get(key);
        System.out.println(Thread.currentThread().getName() + " 读取---作执行完成,Key为:" + key);
        return result;
    }
}

public class ReadWriteLockDemo1 {
    public static void main(String[] args) {
        MyCahe myCahe = new MyCahe();
        // 创建线程放数据
        for (int i = 1; i <= 3; i++) {
            final int num = i;
            new Thread(() -> {
                myCahe.put(num + "", num + "");
            }, String.valueOf(i)).start();
        }
        // 创建线程取数据
        for (int i = 1; i <= 3; i++) {
            final int num = i;
            new Thread(() -> {
                myCahe.get(num + "");
            }, String.valueOf(i)).start();
        }
    }
}

运行结果:

 

2.加入读写锁

package com.codetip.codejuc.juc.readOrWrite;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

// 资源类
class MyCahe {
    // 创建Map集合
    private volatile Map<String, Object> map = new HashMap<>();

    //创建读写锁的对象
    ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    // 存数据
    public void put(String key, Object value) {
        // 添加写锁
        readWriteLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在写+++,Key为:" + key);
            TimeUnit.MICROSECONDS.sleep(300);
            // 放数据
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + " 写+++执行完成,Key为:" + key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放写锁
            readWriteLock.writeLock().unlock();
        }
    }

    // 读取数据
    public Object get(String key) {
        Object result = null;
        // 添加读锁
        readWriteLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 正在读取---,Key为:" + key);
            TimeUnit.MICROSECONDS.sleep(300);
            // 放数据
            result = map.get(key);
            System.out.println(Thread.currentThread().getName() + " 读取---作执行完成,Key为:" + key);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 释放读锁
            readWriteLock.readLock().unlock();
        }
        return result;

    }
}

public class ReadWriteLockDemo1 {
    public static void main(String[] args) {
        MyCahe myCahe = new MyCahe();
        // 创建线程放数据
        for (int i = 1; i <= 3; i++) {
            final int num = i;
            new Thread(() -> {
                myCahe.put(num + "", num + "");
            }, String.valueOf(i)).start();
        }
        // 创建线程取数据
        for (int i = 1; i <= 3; i++) {
            final int num = i;
            new Thread(() -> {
                myCahe.get(num + "");
            }, String.valueOf(i)).start();
        }
    }
}


执行效果:

 

5、深入读写锁

  • 读写锁:一个资源可以被多个读线程同时访问或者可以被一个写线程访问,但不同同时存在读写的线程,读写互斥,读读是共享的

读写锁演变的过程:

第一无锁第二:添加锁第三:读写锁ReentrantReadWriteLock
多线程抢夺资源(乱)使用synchronized和ReentrantLock读读可以共享,提升性能,同时多人可以读操作
都是独享的,每次只能一个操作写还是一个
缺点:读一缺点:1:造成锁饥饿(一直读,没有写的操作)
写一2:读的时候,不能写,只有读完成后,才可以写操作

6、锁降级

  • 将写入锁降级为读锁,写的权限大于读的权限。

    • 只能从写锁降级为读锁。 不能从读锁升级为读锁
  • 降级过程:

 注):此为MarkDown中的mermaid语法,运行结果就是上图

flowchart LR
    id1[获取写锁]-->id2[获取读锁]-->
    id3[释放写锁]-->id4[释放读锁]
package com.codetip.codejuc.juc.readOrWrite;
import java.util.concurrent.locks.ReentrantReadWriteLock;
// 锁降级演示
public class LockDemotion {
    public static void main(String[] args) {
        // 创建一个可重入锁
        ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
        // 写锁
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        // 读锁
        ReentrantReadWriteLock.ReadLock readLock = readWriteLock.readLock();
        try {
            // 获取到写锁
            writeLock.lock();
            System.out.println("+++++正在写操作!!!!");
            // 获取读锁
            readLock.lock();
            System.out.println("-----正在读操作!!!!");
        } finally {
            // 释放写锁
            writeLock.unlock();
            // 释放读锁
            readLock.unlock();
        }
    }
}

10、BlockingQueue阻塞队列

1、先回顾数据结构

队列
先进先出先进后出

2、阻塞队列:

  1. 顾名思义,首先它是一个队列,通过一个共享的队列,可以使得数据有队列一端输入,从另一端输出。
  • 当队列是空的,从队列中获取元素的操作将会被阻塞,直到其他线程往空的队列插入新的元素
  • 当队列是满的,在队列中添加元素的操作将会被阻塞,直到其他线程从队列中移除一个或多个元素或者全部清空,使得队列空闲起来后并后续新增。

在多线程中,所谓的阻塞,在某种情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒执行

 

 注):此为MarkDown中的mermaid语法,运行结果就是上图

flowchart LR
Thread1-->|Put|BlockingQueue-->|Take|Thread2

3、阻塞队列的代码架构

java.util.concurrent 接口 BlockingQueue

  • 类型参数:

    E - 在此 collection 中保持的元素类型

  • 所有超级接口:

    Collection, Iterable, Queue

  • 所有已知子接口:

    BlockingDeque

  • 所有已知实现类:

    ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue

4、阻塞队列的分类

队列类名称解释
ArrayBlockingQueue(常用)由数组结构组成的有界阻塞队列
LinkedBlockingQueue(常用)由链表结构组成的有界阻塞队列(默认大小为:Integer。MAX_VALUE)
DelayQueue使用优先级队列实现的延迟无界的阻塞队列
PriorityBlockingQueue支持优先级排序的无界阻塞队列
SynchronizedQueue不存储元素的阻塞队列,也即单个元素的队列
LinkedTransferQueue由链表组成的无界阻塞队列
LinkedBlockingDeque由链表组成的双向阻塞队列

  • 队列的核心方法
方法类型抛出异常特殊值阻塞超时
插入add(e)offer(e)put(e)offer(time,unit)
移除remove(e)poll()take()poll(time,unit)
检查element(e)peek()不可用不可用

名称描述
抛出异常当队列满时:再往队列add元素,会抛出:java.lang.IllegalStateException: Queue full 异常
当队列空时:在从队列里remove元素,会抛出:main" java.util.NoSuchElementException 异常
特殊值插入方法:成功返回true失败false,移除方法:成功返回队列中的元素,内有则返回nul
阻塞当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产者线程,直到put数据or相应中断退出
当阻塞队列空时,消费者线程试图从队列里take元素,队列会一直阻塞消费者线程直到队列可用
超时当阻塞队列满时,队列阻塞生产者线程一定时间,超过限定时间后,生产者线程会退出

5、入门案例

package com.codetip.codejuc.juc.BlockingQueue;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

public class BlockingQueueDemo01 {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个定长的租所队列
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
        // 第一组案例
//        System.out.println(blockingQueue.add("a"));
//        System.out.println(blockingQueue.add("b"));
//        System.out.println(blockingQueue.add("c"));
//        System.out.println(blockingQueue.add("d"));
//        System.out.println(blockingQueue.element());
//        System.out.println(blockingQueue.remove());

        // 第二组
//        System.out.println(blockingQueue.offer("a"));
//        System.out.println(blockingQueue.offer("b"));
//        System.out.println(blockingQueue.offer("c"));
//        System.out.println(blockingQueue.offer("d"));
//
//        System.out.println(blockingQueue.poll());
//        System.out.println(blockingQueue.poll());
//        System.out.println(blockingQueue.poll());
//        System.out.println(blockingQueue.poll());

        // 第三组
//        blockingQueue.put("a");
//        blockingQueue.put("b");
//        blockingQueue.put("c");
//        blockingQueue.put("d");
//        blockingQueue.take();
//        blockingQueue.take();
//        blockingQueue.take();
//        blockingQueue.take();

        // 第四组
        System.out.println(blockingQueue.offer("a", 2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("b", 2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("c", 2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.offer("d", 2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));
        System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));


    }
}

11、ThreadPool线程池

1、线程池概述

  • 概念:

    • 一种线程使用模式。线程过多会带来过渡开销,进而影响缓存局部性的和整体性能。而线程池内维护着多个线程,等待着监督管理者分配可执行的任务,这就避免了在处理短时间任务是创建和销毁线程的代价,线程池不仅能保证内核的充分利用,还能防止过分调度。

2、线程池架构

 

 

 注):此为MarkDown中的mermaid语法,运行结果就是上图

 classDiagram
 	class 工具类Executors{
	}
  接口Execure <|-- 接口ExecutorService : 继承
  接口ExecutorService <|.. 抽闲类AbstractExecutoService : 实现
  抽闲类AbstractExecutoService <|-- 实现类ThreadPoolExecutor : 继承
  实现类ThreadPoolExecutor <|.. 实现类ThreadPoolExecutor
  实现类ThreadPoolExecutor <|-- 实现类ScheduleThreadPoolExecutor  : 继承
  
  接口ExecutorService <|-- 接口ScheduleExecutoService : 继承
  接口ScheduleExecutoService <|.. 实现类ScheduleThreadPoolExecutor: 实现

3、线程池使用方式

使用Executors工具类创建线程池

方法描述
Executors.newFixedThreadPool一个固定大小的线程池
Executors.newSingleThreadExecutor一个任务一个任务的执行,一个线程池一个任务。
Executors.newCachedThreadPool线程池根据需求创建线程,可扩容,遇强则强

代码演示

package com.codetip.codejuc.juc;

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

public class ThreadPoolDemo001 {
    public static void main(String[] args) {
        // 一个池子五个线程
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        try {
            for (int i = 1; i <= 10; i++) {
                threadPool.execute(() -> System.out.println(Thread.currentThread().getName() + "  正在办理业务!!!"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }
        System.out.println("-------------------------分割线-----------------------");
        // 一池一线程
        ExecutorService threadSingle = Executors.newSingleThreadExecutor();
        try {
            for (int i = 1; i <= 10; i++) {
                threadSingle.execute(() -> System.out.println(Thread.currentThread().getName() + "  正在办理业务!!!"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadSingle.shutdown();
        }
        System.out.println("-------------------------分割线-----------------------");
        // 可缓冲的线程
        ExecutorService executorService = Executors.newCachedThreadPool();
        try {
            for (int i = 1; i <= 10; i++) {
                executorService.execute(() -> System.out.println(Thread.currentThread().getName() + "  正在办理业务!!!"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            executorService.shutdown();
        }
    }
}

线程池底层原理

以上的通过Executors创建的线程池的方法,查看源码发现都是使用的:ThreadPoolExecutor 这个方法

4、线程池的七个参数

  • ThreadPoolExecutor 参数说明

    类名:ThreadPoolExecutor说明
    int corePoolSize常驻(核心)线程数量
    int maximumPoolSize最大线程数量
    Long keepAliveTime线程的存活时间(线程多长时间不用就销毁掉,保留核心线程数量)
    TimeUnit unit存活时间的单位
    BlockingQueue workQueue阻塞队列
    ThreadFactory threadFactory线程工程
    RejectedExecutionHandler handler拒绝策略

5、线程池底层工作流程

 

  • 执行流程

    • 第一:提交任务调用execute, 第二:核心线程会执行任务;第三:任务很多,核心线程执行同时执行不了,会放入阻塞队列中去

    • 第四步:阻塞队列如果满了则会创建新的线程执行任务,线程总数不会大于设定的总线程数。

    • 第五步:继续有新的任务,也没有新的线程可以创建了,会执行拒绝策略。

      • 内置的决绝策略

        决绝策略描述
        AbortPolicy默认,直接抛出RejectedExecutionHandler异常,组织系统的正常运行
        CallerRunsPolicy”调用者运行“运行一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某学任务回退到调用者,从而降低新任务的流量
        DiscardOldestPolicy抛弃队列中等待最久的任务,然后把当前任务加入队列中并尝试再次提交任务
        DiscardPolicy该策略直接丢弃无法处理的任务,不予任何处理也不抛出任何异常,如果允许丢失,这是最好的一种策

      重要,在项目中创建多线程,常见的Executors工具类创建的线程池的方式,都不推荐使用,而是通过ThreadPoolExecutor的方式自己创建线程池,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

      Executors创建线程池的对象的风险和弊端如下

      方法说明
      FixedThreadPool和SingleThreadExecutor允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM
      CachedThreadPool和ScheduledThreadPool允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM

6、自定义线程池

代码如下:

package com.codetip.codejuc.juc.ThreadPool;

import java.util.concurrent.*;

// 创建自定义线程池
public class ThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService service = new ThreadPoolExecutor(
                5,// 核心线程大小
                7, // 最大线程数量
                2L, // 线程的存活时间(线程多长时间不用就销毁掉,保留核心线程数量)
                TimeUnit.SECONDS,// 存活时间单位
                new ArrayBlockingQueue<>(3),// 阻塞队列
                Executors.defaultThreadFactory(), // 线程工程
                 new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
        );

        try {
            for (int i = 1; i <= 10; i++) {
                service.execute(() -> System.out.println(Thread.currentThread().getName() + "  正在办理业务!!!"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            service.shutdown();
        }





    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值