java实现多线程的三种方式

multithread_state
总结实现多线程的三种方式。(上图为多线程的生命周期图,本文不涉及,总结的很好,我就贴过来了…)

三种方式

  1. 通过继承 Thread 类本身;
  2. 通过实现 Runnable 接口;
  3. 通过 Callable 和 Future 创建线程。

1,2无返回值,3返回future对象。其实Thread中的run方法调用的是Runnable接口的run方法。Thread和Runnable都实现了run方法,这种操作模式其实就是代理模式。

2相对于1灵活性更好,因为接口可以多实现,同时还可以继承其他类,而1无法完成;而且2方便共享资源:同一份资源,多个代理访问。

直接集成Thread类

在Java中负责线程这个功能的是Java.lang.Thread这个类。 可以通过创建Thread的实例来创建新的线程。 每个线程都是通个某个特定Thread对象所对应的方法run()来完成其操作的,方法run()称为线程体。 通过调用Thread类的start()方法来启动一个线程。

代码大致框架如下:

class 类名 extends Thread{
属性1
属性2
方法1;
方法2
public void run(){
// other code…
}
}

一个小例子,每个线程进行5次打印。

package thread;
class TestThread extends Thread {
public void run() {
for (int i = 0; i < 100; i++) {
if (count > 0) {
System.out.println("count= " + count--);
}
else{
break;
}
}
}
public static void main(String[] args) {
TestThread h1 = new TestThread();
TestThread h2 = new TestThread();
TestThread h3 = new TestThread();
h1.start();
h2.start();
h3.start();
}
private int count = 5;
}

某次的运行结果:

h2: count= 5
h2: count= 4
h2: count= 3
h2: count= 2
h2: count= 1
h1: count= 5
h1: count= 4
h1: count= 3
h1: count= 2
h1: count= 1
h3: count= 5
h3: count= 4
h3: count= 3
h3: count= 2
h3: count= 1

实现Runnable接口

代码大致框架如下:

class 类名 implements Runnable{
属性1
属性2
方法1;
方法2
public void run(){
// other code…
}
}

一个小例子,实现资源共享:三个卖票窗口,一起来卖5张票。

package thread;
/**
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个【相同】的程序代码的线程去处理【同一】个资源
2):可以跨过java中的单继承的限制,接口可以多实现
*/
class TestRunnable implements Runnable {
private int ticket = 5; // 5张票
public void run() {
while(ticket>0){
synchronized (this){
if(ticket>0){
String nameString = Thread.currentThread().getName();
System.out.println(name + "正在卖票" + this.ticket--);
try {
Thread.sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
TestRunnable t = new TestRunnable();
new Thread(t, "1号窗口").start();
new Thread(t, "2号窗口").start();
new Thread(t, "3号窗口").start();
}
}

某次的运行结果:

1号窗口正在卖票5
3号窗口正在卖票4
2号窗口正在卖票3
2号窗口正在卖票2
3号窗口正在卖票1

实现Callable接口

Callable和Runnable的不同

Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable接口的类都是可被其它线程执行的任务 。Callable和Runnable有几点不同:

  • Callable规定的方法是call(),而Runnable规定的方法是run()
  • call()方法可抛出异常,而run()方法不能抛出异常
  • Callable的任务执行后可返回值,运行Callable任务可得到一个Future对象,而Runnable的任务无返回值。Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。

使用思路

通过FutureTask对象

创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。

使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。

调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。

package thread;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class TestCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int i = 0;
for (; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
return i;
}
public static void main(String[] args) {
TestCallable tCallable = new TestCallable();
FutureTask<Integer> fTask = new FutureTask<Integer>(tCallable);
for (int i = 0; i < 8; i++) {
System.out.println(Thread.currentThread().getName() + " 的循环变量i的值" + i);
if (i == 3) {
new Thread(fTask, "有返回值的线程").start();
}
}
try {
System.out.println("有返回值的线程的返回值:" + fTask.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}

某次运行结果:

main 的循环变量i的值0
main 的循环变量i的值1
main 的循环变量i的值2
main 的循环变量i的值3
main 的循环变量i的值4
main 的循环变量i的值5
main 的循环变量i的值6
有返回值的线程 0
main 的循环变量i的值7
有返回值的线程 1
有返回值的线程 2
有返回值的线程 3
有返回值的线程 4
有返回值的线程 5
有返回值的线程 6
有返回值的线程 7
有返回值的线程 8
有返回值的线程 9
有返回值的线程的返回值:10

通过Executor

Executor框架是Java5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动、执行和关闭,可以简化并发编程的操作。因此,在Java 5之后,通过Executor来启动线程比使用Thread的start方法更好。参考:Executor框架与线程池

创建:Callable实现类 +重写call

借助ExecutorService:执行调度服务ExecutorService,获取Future对象

ExecutorService ser = Executors.newFixedThreadPool(2);
Future result = ser.submit(new CallableImpl()) (submit有返回值,execute无返回值)

获取值:result.get()

停止服务:ser.shutdownNow();

package thread;
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 TestCallable implements Callable<Integer> {
private boolean flag = true;
public void setFlag(boolean flag) {
this.flag = flag;
}
@Override
public Integer call() throws Exception {
int i = 0;
while(flag){
i++;
System.out.println(Thread.currentThread().getName() + " " + i);
Thread.yield();
}
return i;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
TestCallable tCallable1 = new TestCallable();
TestCallable tCallable2 = new TestCallable();
ExecutorService executorService = Executors.newFixedThreadPool(3);
Future<Integer> result1 = executorService.submit(tCallable1);
Future<Integer> result2 = executorService.submit(tCallable1);
Thread.sleep(500);
tCallable1.setFlag(false);
tCallable2.setFlag(false);
System.out.println("tCallable1: "+result1.get());
System.out.println("tCallable2: "+result2.get());
executorService.shutdownNow();
}
}

某次运行的部分结果:

......
pool-1-thread-1 12215
pool-1-thread-1 12216
pool-1-thread-2 14854
pool-1-thread-2 14855
pool-1-thread-2 14856
pool-1-thread-2 14857
pool-1-thread-2 14858
pool-1-thread-2 14859
pool-1-thread-2 14860
pool-1-thread-2 14861
pool-1-thread-2 14862
pool-1-thread-2 14863
pool-1-thread-2 14864
pool-1-thread-2 14865
pool-1-thread-2 14866
pool-1-thread-2 14867
pool-1-thread-2 14868
pool-1-thread-2 14869
pool-1-thread-1 12217
tCallable1: 12217
tCallable2: 14869
------ 本文结束 ------