Java 实现多线程的四种方式 超详细
Java 实现多线程的四种方式
文章目录
- Java 实现多线程的四种方式
- 一、继承 Thread 类
- 二、实现 Runnable 接口
- 三、实现 Callable 接口
- 四、线程池
- 1,Executors.newCachedThreadPool()
- 2,Executors.newFixedThreadPool(3)
- 3,Executors.newScheduledThreadPool(3)
- 4,newSingleThreadExecutor()
- 5.Executors.newWorkStealingPool()
- 线程池核心的参数
- 线程池四种拒绝任务策略
- 工作队列
- 测试ThreadPoolExecutor对线程的执行顺序及拒绝策略
- 继承 Thread 类
- 实现 Runnable 接口
- 实现 Callable 接口
- 线程池
下面我将对这四种方式进行入门级的解析和演示。
一、继承 Thread 类
通过继承 Thread 类实现多线程的步骤如下:
创建 MyThread 类,让其继承 Thread 类并重写 run() 方法。
创建 MyThread 类的实例对象,即创建一个新线程。
调用 start() 方法,启动线程。
public class MyThread extends Thread {@Overridepublic void run() {System.out.println("我是通过继承 Thread 类创建的多线程,我叫" + Thread.currentThread().getName());}
}class TestMyThread {public static void main(String[] args) {MyThread myThread1 = new MyThread();myThread1.setName("Thread-1");MyThread myThread2 = new MyThread();myThread2.setName("Thread-2");MyThread myThread3 = new MyThread();myThread3.setName("Thread-3");myThread1.start();myThread2.start();myThread3.start();}
}
为了演示线程执行顺序的随机性,我特意创建了三个线程,并为每一个线程命名,下面是我运行3次程序的执行结果:
// 第一次
我是通过继承 Thread 类创建的多线程,我叫Thread-2
我是通过继承 Thread 类创建的多线程,我叫Thread-1
我是通过继承 Thread 类创建的多线程,我叫Thread-3// 第二次
我是通过继承 Thread 类创建的多线程,我叫Thread-1
我是通过继承 Thread 类创建的多线程,我叫Thread-3
我是通过继承 Thread 类创建的多线程,我叫Thread-2// 第三次
我是通过继承 Thread 类创建的多线程,我叫Thread-1
我是通过继承 Thread 类创建的多线程,我叫Thread-3
我是通过继承 Thread 类创建的多线程,我叫Thread-2
从上面的执行结果我们可以看到线程的执行顺序和代码中编写的顺序没有关系,线程的执行顺序是具有随机性的。
二、实现 Runnable 接口
Runnable 接口只有一个 run() 方法,源码如下:
public interface Runnable {public abstract void run();
}
通过实现 Runnable 接口实现多线程的步骤如下:
创建 MyRunnable 类实现 Runnable 接口。
创建 MyRunnable 类的实例对象 myRunnable 。
把实例对象 myRunnable 作为参数来创建 Thread 类的实例对象 thread,实例对象 thread 就是一个新线程。
调用 start() 方法,启动线程。
代码示例如下:
public class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("我是通过实现 Runnable 接口创建的多线程,我叫" + Thread.currentThread().getName());}
}class TestMyRunnable {public static void main(String[] args) {MyRunnable myRunnable = new MyRunnable();Thread thread = new Thread(myRunnable);thread.start();}
}
执行结果如下:
我是通过实现 Runnable 接口创建的多线程,我叫Thread-0
相比于继承 Thread 类的方法来说,实现 Runnable 接口是一个更好地选择,因为 Java 不支持多继承,但是可以实现多个接口。
有一点值得注意的是 Thread 类也实现了 Runnable 接口,这意味着构造函数 Thread(Runnable target) 不仅可以传入 Runnable 接口的对象,而且可以传入一个 Thread 类的对象,这样就可以将一个 Thread 对象中的 run() 方法交由其他线程进行调用。
三、实现 Callable 接口
Callable 接口只有一个 call() 方法,源码如下:
public interface Callable<V> {V call() throws Exception;
}
从源码我们可以看到 Callable 接口和 Runnable 接口类似,它们之间的区别在于 run() 方法没有返回值,而 call() 方法是有返回值的。
通过实现 Callable 接口实现多线程的步骤如下:
创建 MyCallable 类实现 Callable 接口。
创建 MyCallable 类的实例对象 myCallable。
把实例对象 myCallable 作为参数来创建 FutureTask 类的实例对象 futureTask。
把实例对象 futureTask 作为参数来创建 Thread 类的实例对象 thread,实例对象 thread 就是一个新线程。
调用 start() 方法,启动线程。
代码示例如下:
public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int a = 6;int b = 9;System.out.println("我是通过实现 Callable 接口创建的多线程,我叫" + Thread.currentThread().getName());return a + b;}
}class TestMyCallable {public static void main(String[] args) throws ExecutionException, InterruptedException {MyCallable myCallable = new MyCallable();FutureTask<Integer> futureTask = new FutureTask(myCallable);Thread thread = new Thread(futureTask);thread.start();System.out.println("返回值为:" + futureTask.get());}
}
执行结果如下:
我是通过实现 Callable 接口创建的多线程,我叫Thread-0
返回值为:15
FutureTask 类提供了一个 get() 方法用来获取 call() 方法的返回值,但需要注意的是调用这个方法会导致程序阻塞,必须要等到线程结束后才会得到返回值。
四、线程池
Java通过Executors创建线程池,分别为:
1,Executors.newCachedThreadPool()
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程, 适用于服务器负载较轻,执行很多短期异步任务
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}
2,Executors.newFixedThreadPool(3)
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待,适用于可以预测线程数量的业务中,或者服务器负载较重,对当前线程数量进行限制。
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}
3,Executors.newScheduledThreadPool(3)
创建一个定长线程池,支持定时及周期性任务执行,适用于需要多个后台线程执行周期任务的场景。
public ScheduledThreadPoolExecutor(int corePoolSize) {super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,new DelayedWorkQueue());
}
4,newSingleThreadExecutor()
创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行,适用于需要保证顺序执行各个任务,并且在任意时间点,不会有多个线程时活动的场景。
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>()));
}
5.Executors.newWorkStealingPool()
创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行
public static ExecutorService newWorkStealingPool() {return new ForkJoinPool(Runtime.getRuntime().availableProcessors(),ForkJoinPool.defaultForkJoinWorkerThreadFactory,null, true);}
线程池核心的参数
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler) {this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(), handler);}
一、corePoolSize 线程池核心线程大小
二、maximumPoolSize 线程池最大线程数量
三、keepAliveTime 空闲线程存活时间
四、unit 空闲线程存活时间单位
五、workQueue 工作队列
六、threadFactory 线程工厂
七、handler 拒绝策略
线程池四种拒绝任务策略
1、直接丢弃(DiscardPolicy)
2、丢弃队列中最早的任务(DiscardOldestPolicy)。
3、抛异常(AbortPolicy)
4、将任务分给调用线程来执行(CallerRunsPolicy)。
工作队列
①ArrayBlockingQueue
基于数组的有界阻塞队列,按FIFO排序。新任务进来后,会放到该队列的队尾,有界的数组可以防止资源耗尽问题。当线程池中线程数量达到corePoolSize后,再有新任务进来,则会将任务放入该队列的队尾,等待被调度。如果队列已经是满的,则创建一个新线程,如果线程数量已经达到maxPoolSize,则会执行拒绝策略。
②LinkedBlockingQuene
基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而基本不会去创建新线程直到maxPoolSize(很难达到Interger.MAX这个数),因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
③SynchronousQuene
一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
④PriorityBlockingQueue
具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
public PriorityBlockingQueue(int initialCapacity,Comparator<? super E> comparator) {if (initialCapacity < 1)throw new IllegalArgumentException();this.lock = new ReentrantLock();this.notEmpty = lock.newCondition();this.comparator = comparator;this.queue = new Object[initialCapacity];}
测试ThreadPoolExecutor对线程的执行顺序及拒绝策略
package com.it.test;import java.time.LocalTime;
import java.util.concurrent.*;/*** 测试ThreadPoolExecutor对线程的执行顺序 **/
public class ThreadPoolTest {public static void main(String[] args) {//核心线程数int corePoolSize = 3;//最大线程数int maximumPoolSize = 6;//超过 corePoolSize 线程数量的线程最大空闲时间long keepAliveTime = 2;//以秒为时间单位TimeUnit unit = TimeUnit.SECONDS;//创建工作队列,用于存放提交的等待执行任务BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<Runnable>(3);ThreadPoolExecutor threadPoolExecutor = null;//ExecutorService pool = Executors.newFixedThreadPool(5);try {//创建线程池threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,new ThreadPoolExecutor.CallerRunsPolicy());//循环提交任务for (int i = 0; i < 10; i++) {//提交任务的索引final int index = (i + 1);threadPoolExecutor.submit(() -> {//线程打印输出System.out.println("【" + LocalTime.now() + "】线程 " + Thread.currentThread() + "正在执行任务" + index);try {//模拟线程执行时间Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}});//每个任务提交后休眠500ms再提交下一个任务,用于保证提交顺序Thread.sleep(500);}} catch (InterruptedException e) {e.printStackTrace();} finally {threadPoolExecutor.shutdown();}}
}
测试不同的拒绝策略:修改new ThreadPoolExecutor.CallerRunsPolicy();
CallerRunsPolicy
【10:31:33.086】线程 Thread[pool-1-thread-1,5,main]正在执行任务1
【10:31:33.545】线程 Thread[pool-1-thread-2,5,main]正在执行任务2
【10:31:34.056】线程 Thread[pool-1-thread-3,5,main]正在执行任务3
【10:31:36.103】线程 Thread[pool-1-thread-4,5,main]正在执行任务7
【10:31:36.609】线程 Thread[pool-1-thread-5,5,main]正在执行任务8
【10:31:37.116】线程 Thread[pool-1-thread-6,5,main]正在执行任务9
【10:31:37.625】线程 Thread[main,5,main]正在执行任务10
【10:31:38.090】线程 Thread[pool-1-thread-1,5,main]正在执行任务4
【10:31:38.557】线程 Thread[pool-1-thread-2,5,main]正在执行任务5
【10:31:39.070】线程 Thread[pool-1-thread-3,5,main]正在执行任务6
DisCardPolicy
【10:34:03.585】线程 Thread[pool-1-thread-1,5,main]正在执行任务1
【10:34:04.014】线程 Thread[pool-1-thread-2,5,main]正在执行任务2
【10:34:04.526】线程 Thread[pool-1-thread-3,5,main]正在执行任务3
【10:34:06.573】线程 Thread[pool-1-thread-4,5,main]正在执行任务7
【10:34:07.082】线程 Thread[pool-1-thread-5,5,main]正在执行任务8
【10:34:07.583】线程 Thread[pool-1-thread-6,5,main]正在执行任务9
【10:34:08.594】线程 Thread[pool-1-thread-1,5,main]正在执行任务4
【10:34:09.015】线程 Thread[pool-1-thread-2,5,main]正在执行任务5
【10:34:09.529】线程 Thread[pool-1-thread-3,5,main]正在执行任务6
DiscardOldestPolicy
【10:35:34.600】线程 Thread[pool-1-thread-1,5,main]正在执行任务1
【10:35:35.034】线程 Thread[pool-1-thread-2,5,main]正在执行任务2
【10:35:35.544】线程 Thread[pool-1-thread-3,5,main]正在执行任务3
【10:35:37.581】线程 Thread[pool-1-thread-4,5,main]正在执行任务7
【10:35:38.091】线程 Thread[pool-1-thread-5,5,main]正在执行任务8
【10:35:38.600】线程 Thread[pool-1-thread-6,5,main]正在执行任务9
【10:35:39.617】线程 Thread[pool-1-thread-1,5,main]正在执行任务5
【10:35:40.035】线程 Thread[pool-1-thread-2,5,main]正在执行任务6
【10:35:40.546】线程 Thread[pool-1-thread-3,5,main]正在执行任务10
AbortPolicy
【10:36:38.723】线程 Thread[pool-1-thread-1,5,main]正在执行任务1
【10:36:39.164】线程 Thread[pool-1-thread-2,5,main]正在执行任务2
【10:36:39.675】线程 Thread[pool-1-thread-3,5,main]正在执行任务3
【10:36:41.720】线程 Thread[pool-1-thread-4,5,main]正在执行任务7
【10:36:42.232】线程 Thread[pool-1-thread-5,5,main]正在执行任务8
【10:36:42.744】线程 Thread[pool-1-thread-6,5,main]正在执行任务9
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@77556fd rejected from java.util.concurrent.ThreadPoolExecutor@368239c8[Running, pool size = 6, active threads = 6, queued tasks = 3, completed tasks = 0]at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)at com.formssi.mall.pay.application.ThreadPoolTest.main(ThreadPoolTest.java:44)
【10:36:43.725】线程 Thread[pool-1-thread-1,5,main]正在执行任务4
【10:36:44.178】线程 Thread[pool-1-thread-2,5,main]正在执行任务5
【10:36:44.678】线程 Thread[pool-1-thread-3,5,main]正在执行任务6Process finished with exit code 1
Java 实现多线程的四种方式 超详细相关推荐
- java创建多线程的四种方式
java多线程的创建方式是面试经常会被问到的一个问题,因此在这里我对java创建多线程的四种方式做一个简单的归纳与总结,便于复习. 一.继承Thread类创建多线程 ① 创建一个继承于Thread类的 ...
- Java 实现多线程的四种方式
在 Java 中实现多线程一共有四种方式: 继承 Thread 类 实现 Runnable 接口 实现 Callable 接口 线程池 下面我将对这四种方式进行入门级的解析和演示. 一.继承 Thre ...
- vue-router传参的四种方式超详细
vue路由传参的四种方式 一.router-link路由导航方式传参 父组件:<router-link to="/跳转到的路径/传入的参数"></router-l ...
- 创建多线程的四种方式
创建多线程的四种方式 方式一:继承于Thread类 创建一个继承于Thread类的子类 重写Thread类的run()->将此线程执行的操作声明在run()中 创建Thread类的子类的对象 通 ...
- Java中创建对象的四种方式
为什么80%的码农都做不了架构师?>>> Java中创建对象的四种方式 (1) 用new语句创建对象,这是最常见的创建对象的方法. (2) 运用反射手段,调用java.l ...
- Java 创建类的四种方式
Java 创建类的四种方式 对于上学期已经学习过c++的同学,是不是对另一大编程语言产生了浓厚的兴趣,对于c++的面向对象编程,又和java的面向变量有何区别,下面我们从java四种创建对象来说起. ...
- java怎样输入五个数字打一成语,Java的线程安全四种方式五个等级[1]
Java的线程安全四种方式五个等级[1]以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧! 四种方式 sychronized ...
- android java 多线程,Android多线程的四种方式
当我们启动一个App的时候,Android系统会启动一个Linux Process,该Process包含一个Thread,称为UI Thread或Main Thread.通常一个应用的所有组件都运行在 ...
- java中创建对象的方式有哪些,Java中创建对象的四种方式
四种方式: http://wenku.baidu.com/link?url=mv6VbMd3d-aCkbGMhn6rbLwFbef7v60nRbyA-thP6Y7hqtjiv0K0_kdtfOWaUj ...
最新文章
- wxWidgets:wxMemoryOutputStream类用法
- 26岁,发25篇SCI,当上211教授、博导。
- ZOJ4100 浙江省赛16th Problem A
- Linux crontab定时执行任务
- 59-混沌操作法感悟2.(2015.2.25)
- vscode控制字符引起的问题以及解决思路
- onvif学习笔记10:获取RTSP流地址
- 《TensorFlow 2.0深度学习算法实战教材》学习笔记(七、Kears高层接口)
- 49.把字符串转换成整数
- python 对文件夹的相关操作
- 【图像隐写】基于matlab分层自嵌入数字水印内容认证与恢复【含Matlab源码 1641期】
- 腾讯推出微信公众平台企业服务平台风铃
- 代码模块化编程思想!!
- 大学计算机应用教程马秀麟,大学计算机基础电子教案.docx
- 淘宝上线独立搜索引擎一淘网
- 为什么不要去外包公司?
- 遇到网站被黑,怎么解决?
- 针对ESXI5.5 本地磁盘分区丢失的解决方法,
- DBSCAN聚类算法原理及图解
- 阿里云服务器购买流程