多线程的几种实现方式
多线程2016-05-26
平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发工程师却在这个上面吃了不少苦头。怎么做一套简便的线程开发模式框架让大家从单线程开发快速转入多线程开发,这不是一件简单的事情。
那具体什么是线程呢?首先看看进程是什么,进程就是系统中执行的一个程序,这个程序可以使用内存、处理器、文件系统等相关资源。例如QQ软件、Eclipse、Tomcat等就是一个exe程序,运行启动起来就是一个进程。为什么需要多线程?如果每个进程都是单独处理一件事情不能多个任务同时处理,比如我们打开qq只能和一个人聊天,我们用eclipse开发代码的时候不能编译代码,我们请求tomcat服务时只能服务一个用户请求,那我想我们还在原始社会。多线程的目的就是让一个进程能够同时处理多件事情或者请求。比如现在我们使用的QQ软件可以同时和多个人聊天,我们用eclipse开发代码时还可以编译代码,tomcat可以同时服务多个用户。
1:裸线程
最基本的线程实现方式,不做过多的说明。
extends Thread
implements Runnable
implements Callable
Runnable和Callable的区别是,
(1)Callable规定的方法是call(),Runnable规定的方法是run()。
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得
(3)call方法可以抛出异常,run方法不可以
(4)运行Callable任务可以拿到一个Future对象,表示异步计算的结果。
它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
Talk is cheap, show me the code.(废话少说,放码过来!O(∩_∩)O~)
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class CallableTest implements Callable<String>{
private int id;
public CallableTest(int id){
this.id = id;
}
@Override
public String call() throws Exception {
return "result of CallableTest " + id;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService exec = Executors.newCachedThreadPool();
ArrayList<Future<String>> results = new ArrayList<Future<String>>();
for (int i = 0; i < 10; i++) {
results.add(exec.submit(new CallableTest(i)));
}
for (Future<String> fs : results) {
if (fs.isDone()) {
System.out.println(fs.get());
} else {
System.out.println("Future result is not yet complete");
}
}
exec.shutdown();
}
}
2:Executors创建线程池
Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利。为了编写高效稳定可靠的多线程程序,线程部分的新增内容显得尤为重要。
四种类型的线程池:
(1)newCachedThreadPool
创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
(2)newFixedThreadPool
创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
(3)newScheduledThreadPool
创建一个定长线程池,支持定时及周期性任务执行。
(4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
3:ForkJoinPool fork/join(分叉/结合)并发框架
jdk7新增了并发框架-fork/join框架,在这种框架下,ForkJoinTask代表一个需要执行的任务,真正执行这些任务的线程是放在一个线程池(ForkJoinPool)里面。ForkJoinPool是一个可以执行ForkJoinTask的ExcuteService,与ExcuteService不同的是它采用了work-stealing模式:所有在池中的线程尝试去执行其他线程创建的子任务,这样就很少有线程处于空闲状态,非常高效。
4:Actor模型
Actor,可以看作是一个个独立的实体,他们之间是毫无关联的。但是,他们可以通过消息来通信。一个Actor收到其他Actor的信息后,它可以根据需要作出各种相应。消息的类型可以是任意的,消息的内容也可以是任意的。这点有点像webservice了。只提供接口服务,你不必了解我是如何实现的。
一个Actor如何处理多个Actor的请求呢?它先建立一个消息队列,每次收到消息后,就放入队列,而它每次也从队列中取出消息体来处理。通常我们都使得这个过程是循环的。让Actor可以时刻处理发送来的消息。
Actor模型=数据+行为+消息。
Actor模型内部的状态由自己的行为维护,外部线程不能直接调用对象的行为,必须通过消息才能激发行为,这样就保证Actor内部数据只有被自己修改。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class RunnableTest implements Runnable {
protected int countDown = 10;
private static int taskCount = 0;
private final int id = taskCount++;
public RunnableTest() {
}
public RunnableTest(int countDown) {
this.countDown = countDown;
}
public String status() {
return "#" + id + "(" + (countDown > 0 ? countDown : "LiftOff! ") + ")";
}
@Override
public void run() {
while (countDown-- > 0) {
System.out.print(status());
Thread.yield();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println();
}
public static void main(String[] args) {
ExecutorService exec = Executors.newFixedThreadPool(1);
for (int i = 0; i < 5; i++) {
exec.execute(new RunnableTest());
}
exec.shutdown();
}
}
如果Executors.newFixedThreadPool(1); 执行结果为
#0(9)#0(8)#0(7)#0(6)#0(5)#0(4)#0(3)#0(2)#0(1)#0(LiftOff! )
#1(9)#1(8)#1(7)#1(6)#1(5)#1(4)#1(3)#1(2)#1(1)#1(LiftOff! )
#2(9)#2(8)#2(7)#2(6)#2(5)#2(4)#2(3)#2(2)#2(1)#2(LiftOff! )
#3(9)#3(8)#3(7)#3(6)#3(5)#3(4)#3(3)#3(2)#3(1)#3(LiftOff! )
#4(9)#4(8)#4(7)#4(6)#4(5)#4(4)#4(3)#4(2)#4(1)#4(LiftOff! )
如果Executors.newFixedThreadPool(5); 执行结果为
#1(9)#0(9)#2(9)#3(9)#4(9)#0(8)#2(8)#1(8)#3(8)#4(8)#3(7)#2(7)#4(7)#1(7)#0(7)#1(6)#2(6)#4(6)#0(6)#3(6)#1(5)#4(5)#3(5)#2(5)#0(5)#1(4)#4(4)#3(4)#2(4)#0(4)#1(3)#2(3)#4(3)#3(3)#0(3)#1(2)#4(2)#3(2)#0(2)#2(2)#1(1)#3(1)#4(1)#2(1)#0(1)#1(LiftOff! )#4(LiftOff! )#2(LiftOff! )#0(LiftOff! )#3(LiftOff! )