线程的创建
创建线程方式一
Java中通过继承Thread类来创建并启动多线程的步骤如下:
- 定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表了线程需要完成的任务,因此把run()方法称为线程执行体。
- 创建Thread子类的实例,即创建了线程对象
- 调用线程对象的start()方法来启动该线程
代码示例
package com.company;
class MyThread extends Thread{
@Override
public void run() {
for (int i=0;i<10;++i){
System.out.println(getName()+"正在执行");
}
}
MyThread(String name){
super(name);
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
MyThread myThread=new MyThread("MyThread");
myThread.start();
for (int i=0;i<10;++i){
System.out.println("mainThread 正在执行");
}
}
}
创建线程方式二
采用 java.lang.Runnable 也是非常常见的一种,我们只需要重写run方法即可步骤如下:
- 定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
- 调用线程对象的start()方法来启动线程
代码示例
package com.company;
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i=0;i<10;++i){
System.out.println( Thread.currentThread().getName()+"正在执行");
}
}
}
public class Main {
public static void main(String[] args) throws ClassNotFoundException {
//创建自定义类对象 线程任务对象
MyRunnable myRunnable=new MyRunnable();
//创建线程对象
Thread thread=new Thread(myRunnable,"myThread");
//启动线程
thread.start();
for (int i=0;i<10;++i){
System.out.println("mainThread 正在执行");
}
}
}
Thread 和Runnable的区别
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享
实现Runnable接口比继承Thread类所具有的优势:
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类
扩充:在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用
java命令执行一个类的时候,实际上都会启动一个JVM,每一个JVM其实在就是在操作系统中启动了一个进
程
匿名内部类方式实现线程的创建
使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作
package com.company;
public class Main {
public static void main(String[] args) {
//匿名内部类创建线程任务对象
Runnable runnable=new Runnable(){
@Override
public void run() {
for (int i=0;i<10;++i){
System.out.println( Thread.currentThread().getName()+"正在执行");
}
}
};
//创建线程对象并启动
new Thread(runnable,"myThread").start();
for (int i=0;i<10;++i){
System.out.println("mainThread 正在执行");
}
}
}
线程同步
当我们使用多个线程访问同一资源的时候,且多个线程中对资源有写的操作,就容易出现线程安全问题
例如我们来模拟电影院的售票窗口,实现多个窗口同时卖 “关公大战外星人”这场电影票(多个窗口一起卖这100张票)
代码示例
package com.company;
class MyRunnable implements Runnable{
private int ticket=100;
@Override
public void run() {
while (true){
if (ticket>0){
//卖票时间
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卖票
System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--);
}
}
}
}
public class Main {
public static void main(String[] args) {
//匿名内部类创建线程任务对象
MyRunnable myRunnable=new MyRunnable();
//创建线程对象并启动
new Thread(myRunnable,"窗口1").start();
new Thread(myRunnable,"窗口2").start();
new Thread(myRunnable,"窗口3").start();
}
}
程序中的问题:相同的票数,比如11这张票被卖了两回。 卖了不存在的票,比如0票与-1票,是不存在的,这种问题,几个窗口(线程)票数不同步了,这种问题称为线程不安全
线程安全问题都是由全局变量及静态变量引起的。若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全,
上面的程序就是多个线程对共享资源ticket同时进行写操作引发的问题,也就是线程1还没有对ticket进行写完就切换到了线程2进行读取这就会产生数据错乱
为了保证每个线程都能正常执行原子操作,Java引入了线程同步机制
有三种方式完成同步操作
1 . 同步代码块
synchronized(同步锁){
需要同步操作的代码
}
同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
1. 锁对象 可以是任意类型。
2. 多个线程对象 要使用同一把锁。
注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他的线程只能在外等着
(BLOCKED)。
代码示例
package com.company;
class MyRunnable implements Runnable{
private int ticket=100;
//同步锁
Object lock=new Object();
@Override
public void run() {
while (true){
//同步
synchronized(lock){
if (ticket>0){
//卖票时间
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卖票
System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--);
}
}
}
}
}
public class Main {
public static void main(String[] args) {
//匿名内部类创建线程任务对象
MyRunnable myRunnable=new MyRunnable();
//创建线程对象并启动
new Thread(myRunnable,"窗口1").start();
new Thread(myRunnable,"窗口2").start();
new Thread(myRunnable,"窗口3").start();
}
}
2. 同步方法
同步方法 :使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步锁是谁?
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
代码示例
package com.company;
class MyRunnable implements Runnable{
private int ticket=100;
@Override
public void run() {
while (true){
//同步方法
SellTickets();
}
}
public synchronized void SellTickets(){
if (ticket>0){
//卖票时间
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卖票
System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--);
}
}
}
public class Main {
public static void main(String[] args) {
//匿名内部类创建线程任务对象
MyRunnable myRunnable=new MyRunnable();
//创建线程对象并启动
new Thread(myRunnable,"窗口1").start();
new Thread(myRunnable,"窗口2").start();
new Thread(myRunnable,"窗口3").start();
}
}
- 锁机制
java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象
Lock锁也称同步锁,加锁与释放锁方法化了,如下:
public void lock() :加同步锁。
public void unlock() :释放同步锁
代码示例
package com.company;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class MyRunnable implements Runnable{
private int ticket=50;
Lock lock=new ReentrantLock();
@Override
public void run() {
while (true){
//加锁
lock.lock();
if (ticket>0){
//卖票时间
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//卖票
System.out.println(Thread.currentThread().getName()+"正在卖"+ticket--);
}
//释放锁
lock.unlock();
}
}
}
public class Main {
publi static void main(String[] args) {
//匿名内部类创建线程任务对象
MyRunnable myRunnable=new MyRunnable();
//创建线程对象并启动
new Thread(myRunnable,"窗口1").start();
new Thread(myRunnable,"窗口2").start();
new Thread(myRunnable,"窗口3").start();
}
}
死锁
死锁的原因主要是锁的嵌套导致两个线程互相等待对方释放锁而造成死锁,例如有AB两把锁,线程1的代码是AB锁嵌套 线程2是BA锁嵌套,在线程1获取A锁后切换到线程2进入获取B锁,这样线程1和线程2都在等待对方释放锁而无法向下执行代码
- 代码示例
class Demo {
public static void main(String[] args) {
Object objA = new Object();
Object objB = new Object();
new Thread(()->{
while(true){
synchronized (objA){
//线程一
System.out.println("线程1获取A");
synchronized (objB){
System.out.println("线程1执行");
}
}
}
}).start();
new Thread(()->{
while(true){
synchronized (objB){
//线程二
System.out.println("线程2获取B");
synchronized (objA){
System.out.println("线程2执行");
}
}
}
}).start();
}
}
线程状态概述
- NEW(新建)状态:线程刚被创建,但是并未启动。还没调用start方法
- Runnable(可运行)状态:线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器
- Blocked(锁阻塞)状态:当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态
- Waiting(无限等待)状态:一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒
- TimedWaiting(计时等待)状态:同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep ,Object.wait。
- eminated(被终止)状态:因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。
-
Timed Waiting (计时等待)
- 进入 TIMED_WAITING 状态的一种常见情形是调用的 sleep 方法,单独的线程也可以调用,不一定非要有协
作关系。 - 为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程
中会睡眠 - sleep与锁无关,线程睡眠到期自动苏醒,并返回到Runnable(可运行)状态。
小提示:sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始立刻执行
- 进入 TIMED_WAITING 状态的一种常见情形是调用的 sleep 方法,单独的线程也可以调用,不一定非要有协
-
BLOCKED (锁阻塞)
- 一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态
比如,线程A与线程B代码中使用同一锁,如果线程A获
取到锁,线程A进入到Runnable状态,那么线程B就进入到Blocked锁阻塞状态
- 一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态
-
Waiting (无限等待)
- 一个正在无限期等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态
package com.company;
public class Main {
//同步锁
static Object object=new Object();
public static void main(String[] args) {
//创建线程对象并启动
new Thread(new Runnable(){
@Override
public void run() {
while (true){
//同步
synchronized (object){
try {
System.out.println(Thread.currentThread().getName()+"=== 获取到锁对象,调用wait方法,进入waiting状态,释放锁对象");
//wait 进行到等待状态时并且释放锁对象
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"=== 从waiting状态醒来,获取到锁对象,继续执行了");
}
}
}
},"线程A").start();
new Thread(new Runnable() {
@Override
public void run() {
while (true){
//每隔3秒唤醒一次
try {
//System.out.println(Thread.currentThread().getName()+"等待3秒");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (object){
System.out.println(Thread.currentThread().getName()+"‐‐‐‐‐ 获取到锁对象,调用notify方法,执行同步代码块后释放锁对象");
//notify唤醒后,锁还是自己获取着,只有执行完同步代码块才会释放锁对象
object.notify();
}
}
}
},"线程B").start();
}
}
当多个线程协作时,比如A,B线程,如果A线程在Runnable(可运行)状态中调用了wait()方法那么A线程就进入了Waiting(无限等待)状态,同时失去了同步锁。假如这个时候B线程获取到了同步锁,在运行状态中调用了notify()方法,那么就会将无限等待的A线程唤醒。注意是唤醒,如果获取到锁对象,那么A线程唤醒后就进入Runnable(可运行)状态;如果没有获取锁对象,那么就进入到Blocked(锁阻塞状态)
等待唤醒机制
等待唤醒中的方法
- wait:线程不再活动,不再参与调度,进入 wait set 中,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。它还要等着别的线程执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的线程从wait set 中释放出来,重新进入到调度队列(ready queue)中
- notify:则选取所通知对象的 wait set 中的一个线程释放
- notifyAll:则释放所通知对象的 wait set 上的全部线程
注意:哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因为它当初中断的地方是在同步块内,而此刻它已经不持有锁,所以她需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调用 wait 方法之后的地方恢复执行- 如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态;否则,从 wait set 出来,又进入 entry set,线程就从 WAITING 状态又变成 BLOCKED 状态
调用wait和notify方法需要注意的细节
- wait方法与notify方法必须要由同一个锁对象调用。因为对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。
- wait方法与notify方法是属于Object类的方法的。因为锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。
- wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为必须要通过锁对象调用这2个方法。
生产者消费者代码示例1
package com.company;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
//生产者
class ProducerThread extends Thread{
private Queue<Integer> queue;//共享资源队列
String m_strName;//生产者名字
int m_nMaxSize;//生产最大值
int m_nData = 0;//生产的数据
ProducerThread(String strName,Queue<Integer> queue,int nMaxSize){
super(strName);
this.m_strName=strName;
this.queue=queue;
this.m_nMaxSize=nMaxSize;
}
@Override
public void run() {
while (true){
//同步
synchronized (queue){
//如果生产到了最大值则等待生产,等消费者消费后在继续生产
//用while而不用if是因为有多个生产者的话,被唤醒后的有可能是生产者线程并且这个线程也抢到锁了继续执行的话则会多生产,从而数据有误
while (queue.size()==m_nMaxSize){
try {
System.out.println("生产满了"+m_strName+"进行等待");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(m_strName+"在队列生产数据+"+m_nData);
queue.offer(m_nData++);
queue.notifyAll();//唤醒所有线程
//睡眠好进行查看
try {
Thread.sleep( 3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
//消费者
class ConsumerThread extends Thread{
private Queue<Integer> queue;//共享资源队列
String m_strName;//消费者名字
ConsumerThread(Queue<Integer> queue,String strName){
super(strName);
this.queue=queue;
this.m_strName=strName;
}
@Override
public void run() {
while (true){
//同步
synchronized (queue){
//判断队列是否为空,为空则进行等待,
while (queue.isEmpty()){
try {
System.out.println("消费完了或者队列为空"+m_strName+"进行等待");
queue.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int nData=queue.poll();
System.out.println(m_strName+"从队列中消费数据"+nData);
queue.notifyAll();//通知所有queue锁对象wait的线程
//睡眠好进行查看
try {
Thread.sleep( 3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class Main {
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<Integer>();
new ProducerThread("生产者1号", queue,5 ).start();
new ProducerThread("生产者2号", queue,5 ).start();
new ConsumerThread(queue, "消费者1号").start();
new ConsumerThread(queue, "消费者2号").start();
new ConsumerThread(queue, "消费者3号").start();
new ConsumerThread(queue, "消费者4号").start();
}
}
从结果可以看出,调用notifyAll唤醒的是所有对象(包括生产者和消费者线程)如下图
生产者消费者代码示例2用多个监视器实现
dk1.5以后将同步和锁封装成了对象。
并将操作锁的隐式方式定义到了该对象中,
将隐式动作变成了显示动作。
Lock接口: 出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成现实锁操作。
同时更为灵活。可以一个锁上加上多组监视器。
lock():获取锁。
unlock():释放锁,通常需要定义finally代码块中。
Condition接口:出现替代了Object中的wait notify notifyAll方法。
将这些监视器方法单独进行了封装,变成Condition监视器对象。可以任意锁进行组合。
await();
signal();
signalAll();
代码示例
package com.company;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//生产者
class ProducerThread extends Thread{
private Lock lock;//锁对象
private Condition producer_con;//生产者监视器
private Condition consumer_con;//消费者监视器
private Queue<Integer> queue;//共享资源队列
String m_strName;//生产者名字
int m_nMaxSize;//生产最大值
int m_nData = 0;//生产的数据
ProducerThread(String strName,Queue<Integer> queue,int nMaxSize,Lock lock,Condition producer_con,Condition consumer_con){
super(strName);
this.m_strName=strName;
this.queue=queue;
this.m_nMaxSize=nMaxSize;
this.lock=lock;
this.producer_con=producer_con;
this.consumer_con=consumer_con;
}
@Override
public void run() {
while (true){
//同步
lock.lock();
try {
//如果生产到了最大值则等待生产,等消费者消费后在继续生产
//用while而不用if是因为有多个生产者的话,被唤醒后的有可能是生产者线程并且这个线程也抢到锁了继续执行的话则会多生产,从而数据有误
while (queue.size()==m_nMaxSize){
try {
System.out.println("生产满了"+m_strName+"进行等待");
producer_con.await();//生产线程在producer_con对象的等待队列中
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(m_strName+"在队列生产数据+"+m_nData);
queue.offer(m_nData++);
consumer_con.signal();//唤醒consumer_con等待队列的消费者线程(唤醒所有也只是其中一个工作)
//睡眠好进行查看打印信息
try {
Thread.sleep( 3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
}
}
//消费者
class ConsumerThread extends Thread{
private Lock lock;//锁对象
private Condition producer_con;//生产者监视器
private Condition consumer_con;//消费者监视器
private Queue<Integer> queue;//共享资源队列
String m_strName;//消费者名字
ConsumerThread(Queue<Integer> queue,String strName,Lock lock,Condition producer_con,Condition consumer_con){
super(strName);
this.queue=queue;
this.m_strName=strName;
this.lock=lock;
this.producer_con=producer_con;
this.consumer_con=consumer_con;
}
@Override
public void run() {
while (true){
//同步
lock.lock();
try {
//判断队列是否为空,为空则进行等待,
while (queue.isEmpty()){
try {
System.out.println("消费完了或者队列为空"+m_strName+"进行等待");
consumer_con.await();//消费线程在consumer_con等待队列中
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int nData=queue.poll();
System.out.println(m_strName+"从队列中消费数据"+nData);
producer_con.signal();//通知producer_con等待队列的生产者线程
//睡眠好进行查看
try {
Thread.sleep( 3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} finally {
lock.unlock();
}
}
}
}
public class Main {
public static void main(String[] args) {
// 创建一个锁对象。
Lock lock = new ReentrantLock();
//通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。
Condition producer_con = lock.newCondition();
Condition consumer_con = lock.newCondition();
//创建任务队列
Queue<Integer> queue = new LinkedList<Integer>();
new ProducerThread("生产者1号", queue,5,lock,producer_con,consumer_con ).start();
new ProducerThread("生产者2号", queue,5,lock,producer_con,consumer_con ).start();
new ConsumerThread(queue, "消费者1号",lock,producer_con,consumer_con).start();
new ConsumerThread(queue, "消费者2号",lock,producer_con,consumer_con).start();
new ConsumerThread(queue, "消费者3号",lock,producer_con,consumer_con).start();
new ConsumerThread(queue, "消费者4号",lock,producer_con,consumer_con).start();
}
}
线程池的使用
- Executors类中有个创建线程池的方法如下:
public static ExecutorService newFixedThreadPool(int nThreads) :返回线程池对象。(创建的是有界线程池,也就是池中的线程个数可以指定最大数量)
- 获取到了一个线程池ExecutorService 对象,那么怎么使用呢,在这里定义了一个使用线程池对象的方法如下:
public Future<?> submit(Runnable task) :获取线程池中的某一个线程对象,并执行
Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用。
- 使用线程池中线程对象的步骤:
- 创建线程池对象。
- 创建Runnable接口子类对象。(task)
- 提交Runnable接口子类对象。(take task)
- 关闭线程池(一般不做)
代码示例
package com.company;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
class RunnableImpl implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"创建了一个新的线程执行");
}
}
public class Main {
public static void main(String[] args) {
//1.使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
ExecutorService executorService = Executors.newFixedThreadPool(2);
//3.调用ExecutorService中的方法submit,传递线程任务(实现类),开启线程,执行run方法
executorService.submit(new RunnableImpl());
//线程池会一直开启,使用完了线程,会自动把线程归还给线程池,线程可以继续使用
executorService.submit(new RunnableImpl());
executorService.submit(new RunnableImpl());
//4.调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
//executorService.shutdown();
}
}
自己创建线程池
当任务大于最大线程数+队列最大容量时会最有多种拒绝策略,也就是最后一个参数设置拒绝策略
ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常。是默认的策略
ThreadPoolExecutor.DiscardPolicy: 丢弃任务,但是不抛出异常 这是不推荐的做法。
ThreadPoolExecutor.DiscardOldestPolicy: 抛弃队列中等待最久的任务,哪个任务先添加哪个任务也就是等待久,也就是执行后面添加的任务
ThreadPoolExecutor.CallerRunsPolicy: 调用任务的run()方法绕过线程池直接执行
package cn.zzj;
/*
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize: 核心线程的最大值,不能小于0
maximumPoolSize:最大线程数,不能小于等于0,maximumPoolSize >= corePoolSize
keepAliveTime: 线程最大能空闲多长时间才销毁,不能小于0
unit: 线程最大能空闲多长时间的时间单位
workQueue: 任务队列,不能为null
threadFactory: 创建线程工厂,不能为null
handler: 任务的拒绝策略,不能为null
*/
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(2,
3,
3, //临时线程最多空闲3秒
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 1; i <= 16; ++i) {
int nNum = i;
//使用lambda表达式实现接口对象传参
pool.submit(() -> {
System.out.println(Thread.currentThread().getName() + "执行了任务" + nNum);
});
}
}
}