创建线程的四种方式
1. 继承Thread类
1.1 创建线程类
集成Thread类,并重写run方法
public class MyThead extends Thread {
/**
* 实现run 方法
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
//暂停0.5秒,模仿业务逻辑执行
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程开始执行了:" + System.currentTimeMillis());
}
}
}
1.2 测试方法
创建三个线程
public static void main(String[] args) {
Thread thread = new MyThead();
Thread threa2 = new MyThead();
Thread threa3 = new MyThead();
thread.start();
threa2.start();
threa3.start();
}
1.3 查看执行结果
Thread-2线程开始执行了:1605334456249
Thread-0线程开始执行了:1605334456250
Thread-1线程开始执行了:1605334456250
Thread-1线程开始执行了:1605334456753
Thread-0线程开始执行了:1605334456753
Thread-2线程开始执行了:1605334456753
Thread-2线程开始执行了:1605334457305
Thread-0线程开始执行了:1605334457306
Thread-1线程开始执行了:1605334457306
Thread-0线程开始执行了:1605334457837
Thread-1线程开始执行了:1605334457838
Thread-2线程开始执行了:1605334457838
Thread-0线程开始执行了:1605334458357
Thread-1线程开始执行了:1605334458358
Thread-2线程开始执行了:1605334458358
Thread-2线程开始执行了:1605334458859
Thread-1线程开始执行了:1605334458859
Thread-0线程开始执行了:1605334458859
Thread-1线程开始执行了:1605334459374
Thread-2线程开始执行了:1605334459374
Thread-0线程开始执行了:1605334459374
Thread-0线程开始执行了:1605334459878
Thread-1线程开始执行了:1605334459878
Thread-2线程开始执行了:1605334459878
Thread-0线程开始执行了:1605334460381
Thread-1线程开始执行了:1605334460381
Thread-2线程开始执行了:1605334460381
Thread-0线程开始执行了:1605334460882
Thread-1线程开始执行了:1605334460882
Thread-2线程开始执行了:1605334460882
Process finished with exit code 0
系统时间有重复的,代表实现了并发
2. 实现Runable接口
2.1 创建线程类
实现Runable接口,并重写run方法
public class MyRunable implements Runnable {
/**
* 重写run方法
*/
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
//暂停0.5秒,模仿业务逻辑执行
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程开始执行了:" + System.currentTimeMillis());
}
}
}
2.2 测试方法
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
Thread thread = new Thread(new MyRunable());
thread.start();
}
}
}
2.3 查看执行结果
Thread-2线程开始执行了:1605334552337
Thread-1线程开始执行了:1605334552337
Thread-0线程开始执行了:1605334552337
Thread-0线程开始执行了:1605334552845
Thread-1线程开始执行了:1605334552846
Thread-2线程开始执行了:1605334552846
Thread-2线程开始执行了:1605334553357
Thread-0线程开始执行了:1605334553357
Thread-1线程开始执行了:1605334553357
Thread-0线程开始执行了:1605334553869
Thread-2线程开始执行了:1605334553869
Thread-1线程开始执行了:1605334553869
Thread-2线程开始执行了:1605334554373
Thread-1线程开始执行了:1605334554373
Thread-0线程开始执行了:1605334554373
Thread-2线程开始执行了:1605334554876
Thread-1线程开始执行了:1605334554876
Thread-0线程开始执行了:1605334554876
Thread-0线程开始执行了:1605334555391
Thread-2线程开始执行了:1605334555391
Thread-1线程开始执行了:1605334555391
Thread-1线程开始执行了:1605334555906
Thread-0线程开始执行了:1605334555906
Thread-2线程开始执行了:1605334555906
Thread-0线程开始执行了:1605334556409
Thread-2线程开始执行了:1605334556409
Thread-1线程开始执行了:1605334556409
Thread-2线程开始执行了:1605334556910
Thread-1线程开始执行了:1605334556910
Thread-0线程开始执行了:1605334556910
3. 实现Callable接口
3.1 创建线程类
实现callable接口,并重写cal方法
public class MyCallable implements Callable<Integer> {
/**
* @return
* 重写call方法
* @throws Exception
*/
@Override
public Integer call() throws Exception {
for (int i = 0; i < 10; i++) {
try {
//暂停0.5秒,模仿业务逻辑执行
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程开始执行了:" + System.currentTimeMillis());
}
return 1;
}
}
3.2 测试方法
public class Test {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
}
}
}
3.3 FutureTask 常用方法
3.3.1 get()方法
该方法会阻塞主线程,一直到call()方法执行完毕
public class Test {
public static void main(String[] args) {
List<FutureTask<Integer>> futureList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
futureList.add(futureTask);
Thread thread = new Thread(futureTask);
thread.start();
}
try {
Integer callCount = new Integer(0);
for (FutureTask<Integer> future : futureList) {
Integer integer = future.get();
callCount += integer;
}
System.out.println("所有线程执行完毕。总的执行次数为:"+callCount);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
3.3.2 get()执行测试
Thread-1线程开始执行了:1605336496803
Thread-2线程开始执行了:1605336496803
Thread-0线程开始执行了:1605336496803
Thread-1线程开始执行了:1605336497311
Thread-0线程开始执行了:1605336497311
Thread-2线程开始执行了:1605336497311
Thread-2线程开始执行了:1605336497837
Thread-1线程开始执行了:1605336497837
Thread-0线程开始执行了:1605336497837
Thread-2线程开始执行了:1605336498359
Thread-1线程开始执行了:1605336498359
Thread-0线程开始执行了:1605336498359
Thread-0线程开始执行了:1605336498865
Thread-2线程开始执行了:1605336498865
Thread-1线程开始执行了:1605336498865
Thread-2线程开始执行了:1605336499369
Thread-0线程开始执行了:1605336499369
Thread-1线程开始执行了:1605336499369
Thread-2线程开始执行了:1605336499871
Thread-0线程开始执行了:1605336499871
Thread-1线程开始执行了:1605336499871
Thread-2线程开始执行了:1605336500375
Thread-1线程开始执行了:1605336500375
Thread-0线程开始执行了:1605336500375
Thread-0线程开始执行了:1605336500879
Thread-1线程开始执行了:1605336500879
Thread-2线程开始执行了:1605336500879
Thread-2线程开始执行了:1605336501384
Thread-0线程开始执行了:1605336501384
Thread-1线程开始执行了:1605336501384
所有线程执行完毕。总的执行次数为:3
3.3.3 cancel()方法
- 当 FutureTask 处于未启动状态时,执行 FutureTask.cancel()方法将此任务永远不会执行;
- 当 FutureTask 处于已启动状态时,执行 FutureTask.cancel(true)方法将以中断线程的方式来阻止任务继续进行,如果执行 FutureTask.cancel(false)将不会对正在执行任务的线程有任何影响;
- 当FutureTask处于已完成状态时,执行 FutureTask.cancel(…)方法将返回 false。
public class Test {
public static void main(String[] args) {
List<FutureTask<Integer>> futureList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable());
futureList.add(futureTask);
Thread thread = new Thread(futureTask);
thread.start();
}
Integer futureCount = 0;
for (FutureTask<Integer> future : futureList) {
if(futureCount == 1){
future.cancel(true);
}
futureCount ++;
}
}
}
3.3.4 cancel()执行测试
没有了线程1
Thread-2线程开始执行了:1605337302642
Thread-0线程开始执行了:1605337302642
Thread-2线程开始执行了:1605337303150
Thread-0线程开始执行了:1605337303150
Thread-2线程开始执行了:1605337303655
Thread-0线程开始执行了:1605337303656
Thread-2线程开始执行了:1605337304172
Thread-0线程开始执行了:1605337304191
Thread-2线程开始执行了:1605337304678
Thread-0线程开始执行了:1605337304716
Thread-2线程开始执行了:1605337305181
Thread-0线程开始执行了:1605337305229
Thread-2线程开始执行了:1605337305689
Thread-0线程开始执行了:1605337305737
Thread-2线程开始执行了:1605337306190
Thread-0线程开始执行了:1605337306237
Thread-2线程开始执行了:1605337306696
Thread-0线程开始执行了:1605337306743
Thread-2线程开始执行了:1605337307198
Thread-0线程开始执行了:1605337307245
4. 线程池创建
4.1 newCacheThreadPool
可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
最大线程数:Integer.MAX_VALUE
核心线程数:0
阻塞队列:BlockingQueue 当没有指定时,容量大小 Integer.MAX_VALUE
MyRunable 类
public class MyRunable implements Runnable {
/**
* 实现方法
*/
@Override
public void run() {
for (int i = 0; i < 2; i++) {
/*try {
//暂停0.5秒,模仿业务逻辑执行
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println(Thread.currentThread().getName() + "线程开始执行了:" + System.currentTimeMillis());
}
}
}
测试方法
public class Test {
public static void main(String[] args) {
ExecutorService executorService = Executors.newCachedThreadPool();
for(int i=0;i<10;i++){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService.execute(new MyRunable());
}
executorService.shutdown();
}
}
测试结果
pool-1-thread-1线程开始执行了:1605340746556
pool-1-thread-1线程开始执行了:1605340746556
pool-1-thread-1线程开始执行了:1605340746760
pool-1-thread-1线程开始执行了:1605340746760
pool-1-thread-1线程开始执行了:1605340746970
pool-1-thread-1线程开始执行了:1605340746970
pool-1-thread-1线程开始执行了:1605340747183
pool-1-thread-1线程开始执行了:1605340747183
pool-1-thread-1线程开始执行了:1605340747416
pool-1-thread-1线程开始执行了:1605340747416
pool-1-thread-1线程开始执行了:1605340747633
pool-1-thread-1线程开始执行了:1605340747633
pool-1-thread-1线程开始执行了:1605340747835
pool-1-thread-1线程开始执行了:1605340747835
pool-1-thread-1线程开始执行了:1605340748038
pool-1-thread-1线程开始执行了:1605340748038
pool-1-thread-1线程开始执行了:1605340748272
pool-1-thread-1线程开始执行了:1605340748272
pool-1-thread-1线程开始执行了:1605340748483
pool-1-thread-1线程开始执行了:1605340748483
结论: 全部都是线程pool-1-thread-1执行的,证明了 如果有,就直接使用。如果没有,就建一个新的线程加入池中
4.2 newFixedThreadPool
创建一个可重用固定个数的线程池,以共享的无界队列方式来运行这些线程
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
最大线程数:输入固定数量
核心线程数:输入固定数量
阻塞队列:BlockingQueue 当没有指定时,容量大小 Integer.MAX_VALUE
测试方法
public class Test {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for(int i=0;i<10;i++){
executorService.execute(new MyRunable());
}
executorService.shutdown();
}
}
测试结果
pool-1-thread-1线程开始执行了:1605341196229
pool-1-thread-1线程开始执行了:1605341196230
pool-1-thread-1线程开始执行了:1605341196230
pool-1-thread-1线程开始执行了:1605341196230
pool-1-thread-1线程开始执行了:1605341196230
pool-1-thread-1线程开始执行了:1605341196230
pool-1-thread-2线程开始执行了:1605341196231
pool-1-thread-3线程开始执行了:1605341196231
pool-1-thread-4线程开始执行了:1605341196232
pool-1-thread-5线程开始执行了:1605341196232
结论: 最大创建的线程数是5个
4.3 ScheduledThreadPoolExecutor
创建一个定长线程池,支持定时及周期性任务执行
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
最大线程数:Integer.MAX_VALUE
核心线程数:输入的
阻塞队列:DelayedWorkQueue 默认16,
测试方法
public class Test {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
for(int i=0;i<10;i++){
scheduledThreadPool.scheduleAtFixedRate(new MyRunable(), 1, 3, TimeUnit.SECONDS);
}
}
}
测试结果
pool-1-thread-1线程开始执行了:1605342365607
pool-1-thread-1线程开始执行了:1605342368598
pool-1-thread-2线程开始执行了:1605342371603
结论: 每3秒执行一次
4.4 newSingleThreadExecutor
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
最大线程数:1
核心线程数:1
阻塞队列:LinkedBlockingQueue
测试方法
public class Test {
public static void main(String[] args) {
ExecutorService executorService = Executors.newSingleThreadExecutor();
for(int i=0;i<10;i++){
executorService.execute(new MyRunable());
}
executorService.shutdown();
}
}
pool-1-thread-1线程开始执行了:1605342567332
pool-1-thread-1线程开始执行了:1605342567334
pool-1-thread-1线程开始执行了:1605342567335
pool-1-thread-1线程开始执行了:1605342567335
pool-1-thread-1线程开始执行了:1605342567336
pool-1-thread-1线程开始执行了:1605342567337
pool-1-thread-1线程开始执行了:1605342567337
pool-1-thread-1线程开始执行了:1605342567337
pool-1-thread-1线程开始执行了:1605342567338
pool-1-thread-1线程开始执行了:1605342567338
结论:保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行,始终只有一个线程执行
5. 线程池阻塞队列
5.1 ArrayBlockingQueue
ArrayBlockingQueue(int i):规定大小的BlockingQueue,其构造必须指定大小。其所含的对象是FIFO顺序排序的。
5.2 LinkedBlockingQueue
LinkedBlockingQueue()或者(int i):大小不固定的BlockingQueue,若其构造时指定大小,生成的BlockingQueue有大小限制,不指定大小,其大小有Integer.MAX_VALUE来决定。其所含的对象是FIFO顺序排序的。
5.3 PriorityBlockingQueue
PriorityBlockingQueue()或者(int i):类似于LinkedBlockingQueue,但是其所含对象的排序不是FIFO,而是依据对象的自然顺序或者构造函数的Comparator决定。
5.4 SynchronizedQueue
SynchronizedQueue():特殊的BlockingQueue,对其的操作必须是放和取交替完成。
6. 自定义线程池
由于Executors 创建的线程池,所使用的的阻塞队列 如:BlockingQueue 容量默认为Integer.MAX_VALUE,经常会造成oom ,所以建议线上自定义阻塞队列,自定义线程池
自定义线程池,可以用ThreadPoolExecutor类创建,它有多个构造方法来创建线程池。
常见的构造函数:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue)
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
该队列会创建定长的对象,用非公平重入锁
测试代码
public class Test {
public static void main(String[] args) {
//自定义阻塞队列,最大容量为10
BlockingQueue<Runnable> bq = new ArrayBlockingQueue<Runnable>(10);
// ThreadPoolExecutor:创建自定义线程池,池中保存的线程数为3,允许最大的线程数为6
ThreadPoolExecutor tpe = new ThreadPoolExecutor(3, 6, 50, TimeUnit.MILLISECONDS, bq);
// 创建10个任务
for(int i=0;i<20;i++){
Runnable runable = new MyRunable();
tpe.execute(runable);
System.out.println(i+"》》线程池中有效程数:"+tpe.getActiveCount());
System.out.println(i+"》》当前线程池队列数:"+tpe.getQueue().size()+"\n");
}
tpe.shutdown();
}
}
测试结果
0》》线程池中有效程数:1
0》》当前线程池队列数:0
1》》线程池中有效程数:2
1》》当前线程池队列数:0
2》》线程池中有效程数:3
2》》当前线程池队列数:0
3》》线程池中有效程数:3
3》》当前线程池队列数:1
4》》线程池中有效程数:3
4》》当前线程池队列数:2
5》》线程池中有效程数:3
5》》当前线程池队列数:3
6》》线程池中有效程数:3
6》》当前线程池队列数:4
7》》线程池中有效程数:3
7》》当前线程池队列数:5
8》》线程池中有效程数:3
8》》当前线程池队列数:6
9》》线程池中有效程数:3
9》》当前线程池队列数:7
10》》线程池中有效程数:3
10》》当前线程池队列数:8
11》》线程池中有效程数:3
11》》当前线程池队列数:9
12》》线程池中有效程数:3
12》》当前线程池队列数:10
13》》线程池中有效程数:4
13》》当前线程池队列数:10
14》》线程池中有效程数:5
14》》当前线程池队列数:10
15》》线程池中有效程数:6
15》》当前线程池队列数:10
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.jxs.multithread.threadpool.MyRunable@6f79caec rejected from java.util.concurrent.ThreadPoolExecutor@67117f44[Running, pool size = 6, active threads = 6, queued tasks = 10, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at com.jxs.multithread.threadpool.Test.main(Test.java:17)
结论:
核心线程数为3,最大线程数:6 阻塞队列:10
当执行时,先执行核心线程池的中的线程,当超过核心线程池的数量,便会阻塞到 阻塞队列中,当阻塞队列满了,会用最大线程数的数量, 当最大线程也满了,就会报错