Java多线程的四种实现方式
1.Java多线程实现的方式有四种:
1.继承Thread类,重写run方法
2.实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target
3.通过Callable和FutureTask创建线程
4.通过线程池创建线程
2.Thread实现方式
继承Thread类,重写run()方法,创建Thread对象调用start()方法启动线程。
public class ThreadDemo extends Thread { @Override public void run() { int t = 1; for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " " + (t++)); } } public static void main(String[] args) { ThreadDemo td1 = new ThreadDemo(); ThreadDemo td2 = new ThreadDemo(); td1.setName("Thread1"); td2.setName("Thread2"); td1.start(); td2.start(); } }
结果:
3.Runnable实现方式
实现Runnable接口,实现run()方法,接口的实现类的实例作为Thread的target传入带参的Thread构造函数,调用start()方法启动线程。
public class RunnableDemo implements Runnable { @Override public void run() { int t = 1; for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " " + (t++)); } } public static void main(String[] args) { RunnableDemo rd = new RunnableDemo(); Thread tr1 = new Thread(rd); Thread tr2 = new Thread(rd); tr1.setName("Thread1"); tr2.setName("Thread2"); tr1.start(); tr2.start(); } } }
结果:
3.Callable和FutureTask创建线程实现方式
(1)创建Callable接口的实现类 ,并实现Call方法;
(2)创建Callable实现类的实现,使用FutureTask类包装Callable对象,该FutureTask对象封装了Callable对象的Call方法的返回值 ;
(3)使用FutureTask对象作为Thread对象的target创建并启动线程;
(4)调用FutureTask对象的get()来获取子线程执行结束的返回值。
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class CallableFutureTaskDemo implements Callable<Integer> { @Override public Integer call() throws Exception { int t = 1; for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " " + (t++)); } return t; } public static void main(String[] args) { Callable<Integer> cftd1 = new CallableFutureTaskDemo(); Callable<Integer> cftd2 = new CallableFutureTaskDemo(); FutureTask<Integer> ft1 = new FutureTask<>(cftd1); FutureTask<Integer> ft2 = new FutureTask<>(cftd2); Thread t1 = new Thread(ft1); Thread t2 = new Thread(ft2); t1.setName("Thread1"); t2.setName("Thread2"); t1.start(); t2.start(); try { System.out.println(ft1.get()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
结果:
5.线程池实现方式
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ExecutorDemo implements Runnable { private static int task_num = 2; //任务数量 @Override public void run() { int t = 1; for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + " " + (t++)); } } public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i <task_num; i++) { ExecutorDemo ed = new ExecutorDemo(); executorService.execute(ed); } executorService.shutdown(); } }
结果:
java里面的线程池的顶级接口是Executor,Executor并不是一个线程池,而只是一个执行线程的工具,而真正的线程池是ExecutorService。
java中的有哪些线程池?
1.newCachedThreadPool创建一个可缓存线程池程
2.newFixedThreadPool 创建一个定长线程池
3.newScheduledThreadPool 创建一个定长线程池
4.newSingleThreadExecutor 创建一个单线程化的线程池
这里的例子用到的就是newFixedThreadPool 。
6.总结
(1)实现Runnable接口比继承Thread类更具有优势!Runnable接口适合多个相同的程序代码的线程去处理同一个资源,可以避免java中的单继承的限制,增加程序的健壮性,而且代码可以被多个线程共享,实现代码和数据独立。
(2)使用线程池可以减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务,可以根据系统的承受能力,调整线程池中工作线线程的数量。
(3)实际应用中,可以通过一个boolean标志位来结束线程。
(4)ExecutorService、Callable都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,还有Future接口也是属于这个框架,有了这种特征得到返回值更方便。
执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的值(由泛型决定)了。get()方法是阻塞的,即线程无返回结果,get方法会一直等待。此外,ExecutoreService提供了submit()方法,传递一个Callable或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。