多线程
线程和进程
进程
一个进程是一个正在运行的程序,进程是操作系统资源分配(计算资源,比如CPU,存储)的最小单位
电脑同时运行多个程序,一个时间段上在多个极短的时间片争抢运行,就是争抢内存,而在我们看来就是多个进程同时运行
线程
一个时间片又被分为多个片段,分给每一个线程来执行,而这个片段就是线程,比进程还要小的单位,而这个时间片存在不同的线程,同时这个时间片的时间很短,所以看起来不同的线程操作同时发生
java的main函数就是一个主线程,而在主线程中开启子线程,实现线程的并发
多线程
如果一个进程拥有不止一个线程,这样的程序称为多线程程序
java多线程的实现方式
- 继承Thread类,重写run方法
- 实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
- 通过Callable和FutureTask创建线程
- 通过线程池创建线程
继承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();//开始启用线程,接受信息
}
}
主线程可以开启多个子线程,但是主线程中的代码不影响执行
只有主线程的代码执行完,才会执行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传入
输出:
这种方法较为复杂,通常会使用继承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);
}
}
输出:
而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);
}
}
程序并没有结束,因为线程池并没有结束,可以通过ExecutorService的shutdown
或shutdownNow
关闭
线程状态及周期
常用方法
- currentThread():获取当前线程对象
- setName():设置线程名称
- getName():获取线程名称
- setPriority(int priority):设置线程的优先级。优先级的范围1-10,默认是5
- getState():获取线程的状态
- join():执行线程会阻塞该线程
- sleep():指定休眠时间,单位是毫秒
- start():启动线程,并不一定会执行run方法
- yield():暂定该线程的执行,交出cpu的使用权
线程状态
线程的状态有四种:初始状态、可运行状态、运行状态和终止状态
初始状态:创建了一个线程对象,并不等于在系统中创建了一个新线程。一旦调用完了start方法线程就进入了可运行状态不会回到初始状态,因此在线程对象的整个生命周期中,只能调用一次start方法。如果对线程对象调用了多次start方法,就会出现一个IllegalStateException异常
可运行状态:线程为运行做好了准备,只等获得cpu来运行
运行状态:只能有一个线程处于运行状态
终止状态:当一个线程执行完run方法中的代码,该线程就会进入终止状态
阻塞状态:如果一个线程要进入运行状态,就要获得cpu时间片,但是某种情况下,线程运行不仅需要cpu进行运算,还需要一些别的条件,例如用户输入等待、网络传输,在这种情况下,即使获得cpu时间片,也不能运行,也因此为了不让这些进程占用cpu的时间,会让这些线程进入阻塞状态
同时除了I/O会进入阻塞状态之外,sleep方法也会进入阻塞状态,可以让线程进入“睡眠“状态
还有join方法,添加等待的时间(毫秒)后,就会从阻塞状态转化为可运行状态
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张,这就是线程安全问题
通过关键字synchronized给操作上锁
private synchronized void sale() {
if (trainCount>0){
System.out.println(Thread.currentThread().getName()+"出售第"+(100-trainCount+1)+"张票");
trainCount--;
}
}
线程同步
并行指的是线程同时执行
同步并不是线程同时执行,而是线程不同时执行。同步的本质指的是数据的同步。一般情况下,线程之间是相互独立的,如果都去访问同一个变量,极有可能这个变量会变乱。如果不想数据变乱,应该不让他们同时访问同一个数据变量,这个控制的过程称为线程同步
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(); } } }
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对象的锁标记
如何解决死锁问题
在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对象的锁标记,而执行剩下的代码
可能有多个线程调用对象的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();
}
}
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
主要参数:
- 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(10,20,2,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())
}
}