Java多线程实现的方式有四种
1 继承Thread类,重写run方法
2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
3.通过Callable和FutureTask创建线程
4.通过线程池创建线程
前面两种可以归结为一类:无返回值,原因很简单,通过重写run方法,run方式的返回值是void,所以没有办法返回结果
后面两种可以归结成一类:有返回值,通过Callable接口,就要实现call方法,这个方法的返回值是Object,所以返回的结果可以放在Object对象中
线程实现方式1:继承Thread类的线程实现方式如下:
1.继承Thread类,重写run方法
2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
3.通过Callable和FutureTask创建线程(future.get()接口会阻塞);
4.通过线程池创建线程
前面两种可以归结为一类:无返回值,原因很简单,通过重写run方法,run方式的返回值是void,所以没有办法返回结果
后面两种可以归结成一类:有返回值,通过Callable接口,就要实现call方法,这个方法的返回值是Object,所以返回的结果可以放在Object对象中
方式1:继承Thread类的线程实现方式如下:
package org.example.myThread;
public class ThreadDemo01 extends Thread {
public ThreadDemo01() {
//编写子类的构造方法,可缺省
}
public void run() {
//编写自己的线程代码
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
public static void main(String[] args) {
ThreadDemo01 thread01 = new ThreadDemo01();
ThreadDemo01 thread02 = new ThreadDemo01();
thread01.setName("自定义的线程1");
thread01.start();
thread02.setName("自定义的线程2");
thread02.start();
//main线程
System.out.println(Thread.currentThread().toString());
}
}
自定义的线程2 0
自定义的线程2 1
自定义的线程2 2
Thread[main,5,main]
自定义的线程1 0
自定义的线程1 1
自定义的线程1 2
线程实现方式2:通过实现Runnable接口,实现run方法,接口的实现类的实例作为Thread的target作为参数传入带参的Thread构造函数,通过调用start()方法启动线程
package org.example.myThread;
class MyThread implements Runnable {
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
System.out.println(Thread.currentThread().getName() + "-->我是通过实现接口的线程实现方式!");
}
}
public class ThreadDemo02 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyThread());
t1.start();
//thread 2
new Thread(new MyThread()).start();
//main线程
System.out.println(Thread.currentThread().toString());
}
}
Thread-0 0
Thread-0 1
Thread-0-->我是通过实现接口的线程实现方式!
Thread[main,5,main]
Thread-1 0
Thread-1 1
Thread-1-->我是通过实现接口的线程实现方式!
线程实现方式3:通过Callable和FutureTask创建线程
a:创建Callable接口的实现类 ,并实现Call方法
b:创建Callable实现类的实现,使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的Call方法的返回值
c:使用FutureTask对象作为Thread对象的target创建并启动线程
d:调用FutureTask对象的get()来获取子线程执行结束的返回值
package org.example.myThread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
class Tickets<Object> implements Callable<Integer> {
private int i;
public Tickets(int i) {
this.i = i;
}
public int myFun(int i) {
return i * 10;
}
//重写call方法
@Override
public Integer call() throws Exception {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + "-->我是通过实现Callable接口通过FutureTask包装器来实现的线程");
return myFun(i);
}
}
public class ThreadDemo03 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// TODO Auto-generated method stub
Callable<Integer> oneCallable = new Tickets<Object>(2);
FutureTask<Integer> oneTask = new FutureTask<Integer>(oneCallable);
Thread t = new Thread(oneTask);
t.start();
System.out.println(oneTask.get());
Callable<Integer> callable = new Tickets<Object>(3);
FutureTask<Integer> task = new FutureTask<Integer>(callable);
Thread t1 = new Thread(task);
t1.start();
System.out.println(task.get());
//main线程
System.out.println(Thread.currentThread().toString());
}
}
Thread-0-->我是通过实现Callable接口通过FutureTask包装器来实现的线程
20
Thread-1-->我是通过实现Callable接口通过FutureTask包装器来实现的线程
30
Thread[main,5,main]
ExecutorService、Callable都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,还有Future接口也是属于这个框架,有了这种特征得到返回值就很方便了。
通过分析可以知道,他同样也是实现了Callable接口,实现了Call方法,所以有返回值。这也就是正好符合了前面所说的两种分类
执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。get方法是阻塞的,即:线程无返回结果,get方法会一直等待。
————————————————
线程实现方式4:通过线程池创建线程
创建线程池底层均会调用ThreadPoolExecutor构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
- corePoolSize:核心线程最大数量,通俗点来讲就是,线程池中常驻线程的最大数量。线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程;如果超过corePoolSize,则新建的是非核心线程。
- maximumPoolSize:线程池中运行最大线程数(包括核心线程和非核心线程)
- keepAliveTime:线程池中空闲线程(仅适用于非核心线程)所能存活的最长时间。当需要执行的任务很多,线程池的线程数大于核心池的大小时,keepAliveTime才起作用。
- unit:存活时间单位,与keepAliveTime搭配使用(TimeUnit.DAYS,TimeUnit.HOURS,TimeUnit.MINUTES,TimeUnit.MILLISECONDS,TimeUnit.MICRODECONDS)
- workQueue:存放任务的阻塞队列(维护着等待执行的
- Runnable对象。当所有的核心线程都在干活时,新添加的任务会被添加到这个队列中等待处理,如果队列满了,则新建非核心线程执行任务)。
- handler:线程池饱和策略
线程池处理新提交任务流程
- 判断线程池中核心线程数是否已达阀值corePoolSize,若否,则创建一个新核心线程执行任务
- 若核心线程数已达阀值corePoolSize,判断阻塞队列workQueue是否已满,若未满,则将新任务添加进阻塞队列
- 若已满,判断线程池中线程数是否达到阀值maximumPoolSize,若否,则新建一个非核心线程执行任务。若达到阀值,则执行线程池饱和策略
java创建线程池的四种方式:
newCachedThreadPool 创建一个可缓存的线程池,如果线程池长度超过处理需求,可灵活回收空闲线程,若无可回收,则新建线程
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
newSingleThreadExecutor 创建一个单线程化的线程池,它只会唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO,LIFO,优先级)执行
package org.example.myThread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadDemo05 {
private static int POOL_NUM = 5; //线程池数量
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < POOL_NUM; i++) {
RunnableThread thread = new RunnableThread();
Thread.sleep(100);
executorService.execute(thread);
}
//关闭线程池
executorService.shutdown();
}
}
class RunnableThread implements Runnable {
@Override
public void run() {
System.out.println("通过线程池方式创建的线程:" + Thread.currentThread().getName() + " ");
}
}
通过线程池方式创建的线程:pool-1-thread-1
通过线程池方式创建的线程:pool-1-thread-2
通过线程池方式创建的线程:pool-1-thread-3
通过线程池方式创建的线程:pool-1-thread-1
通过线程池方式创建的线程:pool-1-thread-2
扩展
应用
1.Callable接口
Runnable接口的实现类还是Callable接口的实现类,都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor执行。ThreadPoolExecutor或ScheduledThreadPoolExecutor都实现了 ExcutorService接口, 而因此 Callable需要和Executor框架中的ExcutorService结合使用,我们先看看ExecutorService提供的方法:
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
第一个方法:submit提交一个实现Callable接口的任务,并且返回封装了异步计算结果的Future。
第二个方法:submit提交一个实现Runnable接口的任务,并且指定了在调用Future的get方法时返回的result对象。
第三个方法:submit提交一个实现Runnable接口的任务,并且返回封装了异步计算结果的Future。
因此我们只要创建好我们的线程对象(实现Callable接口或者Runnable接口),然后通过上面3个方法提交给线程池去执行即可。还有点要注意的是,除了我们自己实现Callable对象外,我们还可以使用工厂类Executors来把一个Runnable对象包装成Callable对象。Executors工厂类提供的方法如下:
public static Callable<Object> callable(Runnable task)
public static <T> Callable<T> callable(Runnable task, T result)
2.Future接口
Future接口是用来获取异步计算结果的,就是对具体的Runnable或者Callable对象任务执行的结果进行获取(get()),取消(cancel()),判断是否完成等操作。
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
V get() :获取异步执行的结果,如果没有结果可用,此方法会阻塞直到异步计算完成。
V get(Long timeout , TimeUnit unit) :获取异步执行结果,如果没有结果可用,此方法会阻塞,但是会有时间限制,如果阻塞时间超过设定的timeout时间,该方法将抛出异常。
boolean isDone() :如果任务执行结束,无论是正常结束或是中途取消还是发生异常,都返回true。
boolean isCanceller() :如果任务完成前被取消,则返回true。
boolean cancel(boolean mayInterruptRunning) :
如果任务还没开始,执行cancel(...)方法将返回false;如果任务已经启动,执行cancel(true)方法将以中断执行此任务线程的方式来试图停止任务,如果停止成功,返回true;当任务已经启动,执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时返回false;当任务已经完成,执行cancel(...)方法将返回false。mayInterruptRunning参数表示是否中断执行中的线程。
通过方法分析我们也知道实际上Future提供了3种功能:(1)能够中断执行中的任务(2)判断任务是否执行完成(3)获取任务执行完成后额结果。
我们必须明白Future只是一个接口,我们无法直接创建对象,因此就需要其实现类FutureTask登场啦。
3.FutureTask类
public class FutureTask<V> implements RunnableFuture<V> {}
FutureTask类实现了RunnableFuture接口,看一下RunnableFuture接口的实现:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
分析:FutureTask除了实现了Future接口外还实现了Runnable接口,因此FutureTask也可以直接提交给Executor执行。 当然也可以调用线程直接执行(FutureTask.run())。接下来我们根据FutureTask.run()的执行时机来分析其所处的3种状态:
- (1)未启动,FutureTask.run()方法还没有被执行之前,FutureTask处于未启动状态,当创建一个FutureTask,而且没有执行FutureTask.run()方法前,这个FutureTask也处于未启动状态。
- (2)已启动,FutureTask.run()被执行的过程中,FutureTask处于已启动状态。
- (3)已完成,FutureTask.run()方法执行完正常结束,或者被取消或者抛出异常而结束,FutureTask都处于完成状态。
4.Callable/Future/FutureTask的使用
4.1 使用Callable+Future获取执行结果
public class CallableDemo implements Callable<Integer> {
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子线程开始计算啦!");
Thread.sleep(2000);
for(int i=0 ;i<5000;i++){
sum=sum+i;
}
System.out.println("Callable子线程计算结束!");
return sum;
}
}
------------------------------------------------------------------------------------
public class CallableTest {
public static void main(String[] args) {
//创建线程池
ExecutorService es = Executors.newSingleThreadExecutor();
//创建Callable对象任务
CallableDemo calTask=new CallableDemo();
//提交任务并获取执行结果
Future<Integer> future =es.submit(calTask);
//关闭线程池
es.shutdown();
try {
Thread.sleep(2000);
System.out.println("主线程在执行其他任务");
if(future.get()!=null){
//输出获取到的结果
System.out.println("future.get()-->"+future.get()); //get会被阻塞
}else{
//输出获取到的结果
System.out.println("future.get()未获取到结果");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("主线程在执行完成");
}
}
------------------------------------------------------------------------------------------------------------------------
Callable子线程开始计算啦!
主线程在执行其他任务
Callable子线程计算结束!
future.get()-->12497500
主线程在执行完成
4.2 使用Callable+FutureTask获取执行结果
public class CallableTest {
public static void main(String[] args) {
// //创建线程池
// ExecutorService es = Executors.newSingleThreadExecutor();
// //创建Callable对象任务
// CallableDemo calTask=new CallableDemo();
// //提交任务并获取执行结果
// Future<Integer> future =es.submit(calTask);
// //关闭线程池
// es.shutdown();
//创建线程池
ExecutorService es = Executors.newSingleThreadExecutor();
//创建Callable对象任务
CallableDemo calTask=new CallableDemo();
//创建FutureTask
FutureTask<Integer> futureTask=new FutureTask<>(calTask);
//执行任务
es.submit(futureTask);
//关闭线程池
es.shutdown();
try {
Thread.sleep(2000);
System.out.println("主线程在执行其他任务");
if(futureTask.get()!=null){
//输出获取到的结果
System.out.println("futureTask.get()-->"+futureTask.get());
}else{
//输出获取到的结果
System.out.println("futureTask.get()未获取到结果");
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("主线程在执行完成");
}
}
--------------------------------------------------------------------------------------------------------------------------------------------
Callable子线程开始计算啦!
主线程在执行其他任务
Callable子线程计算结束!
futureTask.get()-->12497500
主线程在执行完成
但其实这两种方法最终是一样的:
第一种方式Callable+Future最终也是以Callable+FutureTask的形式实现的。
在第一种方式中调用了: Future future = executor.submit(task);
那就让我们看看executor.submit(task)的源码吧:
//java.util.concurrent.AbstractExecutorService类中
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);//可以看到源码中其实是在submit(Callable<T> task)内部创建了一个RunnableFuture<T>接口实现类
execute(ftask);
return ftask;
}''
而FutureTask又是RunnableFuture的实现类,那就再看看newTaskFor(Callable callable)里面干了什么:
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
```