Java-JUC(六):创建线程的4种方式
Java创建线程的4种方式:
Java使用Thread类代表线程,所有线程对象都必须是Thread类或者其子类的实例。Java可以用以下4种方式来创建线程:
1)继承Thread类创建线程;
2)实现Runnable接口创建线程;
3)实现Callable接口,通过FutureTask包装器来创建Thread线程;
4)使用ExecutorService、Callable(或者Runnable)、Future实现由返回结果的线程。
接下来,针对这四种方式详细介绍及其用法:
1)继承Thread类创建线程
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extends Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。
例如:
/**
* 无返回值的
*/
class MyThread extends Thread {
CountDownLatch countDownLatch; public MyThread(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
} @Override
public void run() {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ":my thread ");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}
}
main调用:
public static void main(String[] args) {
// 第一種:使用extends Thread方式
CountDownLatch countDownLatch1 = new CountDownLatch(2);
for (int i = 0; i < 2; i++) {
MyThread myThread1 = new MyThread(countDownLatch1);
myThread1.start();
}
try {
countDownLatch1.await();
System.out.println("thread complete...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
2)实现Runnable接口创建线程
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口,如下:
/**
* 无返回值的
*/
class MyRunnable implements Runnable {
CountDownLatch countDownLatch; public MyRunnable(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
} public void run() {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + ":my runnable");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}
}
为了启动MyRunnable,需要首先实例化一个Thread,并传入自己的MyRunnable实例:
public static void main(String[] args) {
// 第二種:使用implements Runnable方式
CountDownLatch countDownLatch2 = new CountDownLatch(2);
MyRunnable myRunnable = new MyRunnable(countDownLatch2);
for (int i = 0; i < 2; i++) {
new Thread(myRunnable).start();
}
try {
countDownLatch2.await();
System.out.println("runnable complete...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:
public void run() {
if (target != null) {
target.run();
}
}
3)实现Callable接口,通过FutureTask包装器来创建Thread线程
Callable接口(也只有一个方法)定义如下:
public interface Callable<V> {
V call() throws Exception;
}
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Callable接口(它是一个具有返回值的),如下:
/**
* 有返回值的
*/
class MyCallable implements Callable<Integer> {
CountDownLatch countDownLatch; public MyCallable(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
} public Integer call() throws Exception {
try {
Thread.sleep(2000); int sum = 0;
for (int i = 0; i <= 100; i++) {
sum += i;
} System.out.println(Thread.currentThread().getName() + ":my callable"); return sum;
} finally {
countDownLatch.countDown();
}
}
}
和Runnable接口不一样,Callable接口提供了一个call()方法作为线程执行体,call()方法比run()方法功能要强大。
1)call()方法可以有返回值
2)call()方法可以声明抛出异常
Java5提供了Future接口来代表Callable接口里call()方法的返回值,并且为Future接口提供了一个实现类FutureTask,这个实现类既实现了Future接口,还实现了Runnable接口,因此可以作为Thread类的target。在Future接口里定义了几个公共方法来控制它关联的Callable任务。
boolean cancel(boolean mayInterruptIfRunning)://视图取消该Future里面关联的Callable任务
V get()://返回Callable里call()//方法的返回值,调用这个方法会导致程序阻塞,必须等到子线程结束后才会得到返回值
V get(long timeout,TimeUnit unit)://返回Callable里call()方法的返回值,最多阻塞timeout时间,经过指定时间没有返回抛出TimeoutException
boolean isDone();//若Callable任务完成,返回True
boolean isCancelled();//如果在Callable任务正常完成前被取消,返回True
介绍了相关的概念之后,创建并启动有返回值的线程的步骤如下:
1】创建Callable接口的实现类,并实现call()方法,然后创建该实现类的实例(从java8开始可以直接使用Lambda表达式创建Callable对象)。
2】使用FutureTask类来包装Callable对象,该FutureTask对象封装了Callable对象的call()方法的返回值
3】使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口)
4】调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
main调用:
public static void main(String[] args) {
// 第三種:使用implements Callable方式,具有返回值
List<FutureTask<Integer>> resultItems1 = new ArrayList<FutureTask<Integer>>();
CountDownLatch countDownLatch3 = new CountDownLatch(2);
for (int i = 0; i < 2; i++) {
MyCallable myCallable = new MyCallable(countDownLatch3);
FutureTask<Integer> futureTask = new FutureTask<Integer>(myCallable);
new Thread(futureTask).start();
resultItems1.add(futureTask);
}
try {
countDownLatch3.await();
Iterator<FutureTask<Integer>> iterator = resultItems1.iterator();
while (iterator.hasNext()) {
try {
System.out.println(iterator.next().get());
} catch (ExecutionException e) {
e.printStackTrace();
}
}
System.out.println("callable complete...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
4)使用ExecutorService、Callable(或者Runnable)、Future实现由返回结果的线程
Executors类,提供了一系列工厂方法用于创建线程池,返回的线程池都实现了ExecutorService接口:
public static ExecutorService newFixedThreadPool(int nThreads) ;//创建固定数目线程的线程池。
public static ExecutorService newCachedThreadPool();//创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。
public static ExecutorService newSingleThreadExecutor();//创建一个单线程化的Executor。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);//创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。
下边以MyCallable为例,展示下具体的用法(MyCallable与3)中的定义一样):
public static void main(String[] args) {
// 第四種:使用使用線程池方式
// 接受返回參數
List<Future> resultItems2 = new ArrayList<Future>();
// 給線程池初始化5個線程
ExecutorService executorService = Executors.newFixedThreadPool(5);
CountDownLatch countDownLatch4 = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
MyCallable myCallable = new MyCallable(countDownLatch4);
Future result = executorService.submit(myCallable);
resultItems2.add(result);
}
// 等待线程池中分配的任务完成后才关闭(关闭之后不允许有新的线程加入,但是它并不会等待线程结束),而executorService.shutdownNow();是立即关闭不管是否线程池中是否有其他未完成的线程。
executorService.shutdown();
try {
countDownLatch4.await();
Iterator<Future> iterator = resultItems2.iterator();
while (iterator.hasNext()) {
try {
System.out.println(iterator.next().get());
} catch (ExecutionException e) {
e.printStackTrace();
}
}
System.out.println("callable complete...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Java-JUC(六):创建线程的4种方式的更多相关文章
- Java多线程之创建线程的三种方式比较
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6560057.html 一:继承Thread类创建线程 1:继承Thread类定义线程子类: 2:重写run( ...
- java多线程之创建线程的4种方式及Future
Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例.Java可以用四种方式来创建线程: 继承Thread创建线程 实现Runnable接口创建线程 实现callab ...
- JAVA·多线程:创建线程的两种方式
1.–扩展java.lang.Thread类 package multiThread; public class Thread02extThread { public static void main ...
- java的线程、创建线程的 3 种方式、静态代理模式、Lambda表达式简化线程
0.介绍 线程:多个任务同时进行,看似多任务同时进行,但实际上一个时间点上我们大脑还是只在做一件事情.程序也是如此,除非多核cpu,不然一个cpu里,在一个时间点里还是只在做一件事,不过速度很快的切换 ...
- 0036 Java学习笔记-多线程-创建线程的三种方式
创建线程 创建线程的三种方式: 继承java.lang.Thread 实现java.lang.Runnable接口 实现java.util.concurrent.Callable接口 所有的线程对象都 ...
- 【java并发】传统线程技术中创建线程的两种方式
传统的线程技术中有两种创建线程的方式:一是继承Thread类,并重写run()方法:二是实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread.这两种方式 ...
- Java并发编程:Java创建线程的三种方式
目录 引言 创建线程的三种方式 一.继承Thread类 二.实现Runnable接口 三.使用Callable和Future创建线程 三种方式的对比 引言 在日常开发工作中,多线程开发可以说是必备技能 ...
- java创建线程的三种方式及其对比
第一种方法:继承Thread类,重写run()方法,run()方法代表线程要执行的任务.第二种方法:实现Runnable接口,重写run()方法,run()方法代表线程要执行的任务.第三种方法:实现c ...
- AJPFX总结java创建线程的三种方式及其对比
Java中创建线程主要有三种方式: 一.继承Thread类创建线程类 (1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务.因此把run()方法称为执行 ...
随机推荐
- CentOS下多网卡绑定多IP段时导致只有一个会通的问题解决
原因:Linux默认开启了反向路由检查导致的,比如说外面访问eth0的网卡,而网关在eth1上,又或者从eth0出的流量,而网关在eth1上,此时会检查到网关不在同一个网卡上导致出不去,进不来的问题. ...
- Redis "MISCONF Redis is configured to save RDB snapshots, but is currently not able to persist on disk"问题的解决(转)
今天第二次遇到Redis “MISCONF Redis is configured to save RDB snapshots, but is currently not able to persis ...
- Visual studio 2010出现“error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏”解决方式
本来自己的电脑上装了VS2010,因为开发Cocos2d-x 3.x,所以就在自己的机器上装了一个VS2012. 但是.这不装不要紧,debug一下自己原来的程序,结果出现了"error L ...
- .NET轻量级ORM组件Dapper修炼手册
一.摘要 1.1.为什么叫本次的分享课叫<修炼手册>? 阿笨希望本次的分享课中涉及覆盖的一些小技巧.小技能给您带来一些帮助.希望您在日后工作中把它作为一本实际技能手册进行储备,以备不时之需 ...
- 树莓派 Windows10 IoT Core 开发教程
入门指引 现在让我们把LED连接到安装了Windows10 IoT Core 的硬件设备,并创建一个应用程序来让它们闪烁. 在Visual Studio中加载工程 首先在这里找到例程,这里有C++和C ...
- concat函數 函數concat 可以用來合拼兩個或以上的字串。
12. “Mexico 墨西哥”的首都是”Mexico City”. 顯示所有國家名字,其首都是國家名字加上”City”. concat函數 函數concat 可以用來合拼兩個或以上的字串. : SE ...
- Android Service总结04 之被绑定的服务 -- Bound Service
Android Service总结04 之被绑定的服务 -- Bound Service 版本 版本说明 发布时间 发布人 V1.0 添加了Service的介绍和示例 2013-03-17 Skywa ...
- 跟踪EBS客户端的IP地址
Meterlink参考文档: How to Track IP Address of the Form Session in Oracle application 11i (文档 ID 878931.1 ...
- Linux学习13-CentOS安装ab做压力测试
前言 网站性能压力测试是服务器网站性能调优过程中必不可缺少的一,测试环境准备好了后,如何对网站做压力测试? 压力测试的工具很多,如:ab.http_load.webbench.siege.jmeter ...
- 使用Spire.Office for .NET(Word、Excel、PPT、PDF等)的初步感受
前言 本文大部分内容来自http://www.codeproject.com/Articles/710747/First-thoughts-on-Spire-Doc-for-NET. 针对我个人来说, ...