最近看了几个多线程设计模式,对照J2SE5.0里的多线程工具,两者很相似。从网上找了几篇J2SE5.0多线程工具例子, 供参考
(转自赛迪网)
Java自1995年面世以来得到了广泛得一个运用,但是对多线程编程的支持Java很长时间一直停留在初级阶段。在Java 5.0之前Java里的多线程编程主要是通过Thread类,Runnable接口,Object对象中的wait()、 notify()、 notifyAll()等方法和synchronized关键词来实现的。这些工具虽然能在大多数情况下解决对共享资源的管理和线程间的调度,但存在以下几个问题
1.
过于原始,拿来就能用的功能有限,即使是要实现简单的多线程功能也需要编写大量的代码。这些工具就像汇编语言一样难以学习和使用,比这更糟糕的是稍有不慎它们还可能被错误地使用,而且这样的错误很难被发现。
2.
如果使用不当,会使程序的运行效率大大降低。
3.
为了提高开发效率,简化编程,开发人员在做项目的时候往往需要写一些共享的工具来实现一些普遍适用的功能。但因为没有规范,相同的工具会被重复地开发,造成资源浪费。
4.
因为锁定的功能是通过
Synchronized
来实现的,这是一种块结构,只能对代码中的一段代码进行锁定,而且锁定是单一的。如以下代码所示:
synchronized
(
lock
)
{
//
执行对共享资源的操作
……
}
|
一些复杂的功能就很难被实现。比如说如果程序需要取得
lock A
和
lock B
来进行操作
1
,然后需要取得
lock C
并且释放
lock A
来进行操作
2
,
Java 5.0
之前的多线程框架就显得无能为力了。
因为这些问题,程序员对旧的框架一直颇有微词。这种情况一直到
Java 5.0
才有较大的改观,一系列的多线程工具包被纳入了标准库文件。这些工具包括了一个新的多线程程序的执行框架,使编程人员可方便地协调和调度线程的运行,并且新加入了一些高性能的常用的工具,使程序更容易编写,运行效率更高。本文将分类并结合例子来介绍这些新加的多线程工具。
在我们开始介绍
Java 5.0
里的新
Concurrent
工具前让我们先来看一下一个用旧的多线程工具编写的程序,这个程序里有一个
Server
线程,它需要启动两个
Component
,
Server
线程需等到
Component
线程完毕后再继续。相同的功能在
Synchronizer
一章里用新加的工具
CountDownLatch
有相同的实现。两个程序,孰优孰劣,哪个程序更容易编写,哪个程序更容易理解,相信大家看过之后不难得出结论。
public class ServerThread {
Object concLock = new Object();
int count = 2;
public void runTwoThreads() {
//
启动两个线程去初始化组件
new Thread(new ComponentThread1(this)).start();
new Thread(new ComponentThread1(this)).start();
// Wait for other thread
while(count != 0) {
synchronized(concLock) {
try {
concLock.wait();
System.out.println("Wake up.");
} catch (InterruptedException ie) { //
处理异常
}
}
}
System.out.println("Server is up.");
}
public void callBack() {
synchronized(concLock) {
count--;
concLock.notifyAll();
}
}
public static void main(String[] args){
ServerThread server = new ServerThread();
server.runTwoThreads();
}
}
public class ComponentThread1 implements Runnable {
private ServerThread server;
public ComponentThread1(ServerThread server) {
this.server = server;
}
public void run() {
//
做组件初始化的工作
System.out.println("Do component initialization.");
server.callBack();
}
}
|
1:三个新加的多线程包
Java 5.0
里新加入了三个多线程包:
java.util.concurrent, java.util.concurrent.atomic, java.util.concurrent.locks.
-
java.util.concurrent
包含了常用的多线程工具,是新的多线程工具的主体。
-
java.util.concurrent.atomic
包含了不用加锁情况下就能改变值的原子变量,比如说
AtomicInteger
提供了
addAndGet()
方法。
Add
和
Get
是两个不同的操作,为了保证别的线程不干扰,以往的做法是先锁定共享的变量,然后在锁定的范围内进行两步操作。但用
AtomicInteger.addAndGet()
就不用担心锁定的事了,其内部实现保证了这两步操作是在原子量级发生的,不会被别的线程干扰。
-
java.util.concurrent.locks
包包含锁定的工具。
2:Callable 和 Future接口
Callable
是类似于
Runnable
的接口,实现
Callable
接口的类和实现
Runnable
的类都是可被其它线程执行的任务。
Callable
和
Runnable
有几点不同:
-
Callable
规定的方法是
call()
,而
Runnable
规定的方法是
run().
-
Callable
的任务执行后可返回值,而
Runnable
的任务是不能返回值的。
-
call
()方法可抛出异常,而
run
()方法是不能抛出异常的。
-
运行
Callable
任务可拿到一个
Future
对象,通过
Future
对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
以下是
Callable
的一个例子:
public class DoCallStuff implements Callable{ // *1
private int aInt;
public DoCallStuff(int aInt) {
this.aInt = aInt;
}
public String call() throws Exception { //*2
boolean resultOk = false;
if(aInt == 0){
resultOk = true;
} else if(aInt == 1){
while(true){ //infinite loop
System.out.println("looping....");
Thread.sleep(3000);
}
} else {
throw new Exception("Callable terminated with Exception!"); //*3
}
if(resultOk){
return "Task done.";
} else {
return "Task failed";
}
}
}
|
*1:
名为
DoCallStuff
类实现了
Callable
,
String
将是
call
方法的返回值类型。例子中用了
String
,但可以是任何
Java
类。
*2: call
方法的返回值类型为
String
,这是和类的定义相对应的。并且可以抛出异常。
*3: call
方法可以抛出异常,如加重的斜体字所示。
以下是调用
DoCallStuff
的主程序。
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Executor {
public static void main(String[] args){
//*1
DoCallStuff call1 = new DoCallStuff(0);
DoCallStuff call2 = new DoCallStuff(1);
DoCallStuff call3 = new DoCallStuff(2);
//*2
ExecutorService es = Executors.newFixedThreadPool(3);
//*3
Future future1 = es.submit(call1);
Future future2 = es.submit(call2);
Future future3 = es.submit(call3);
try {
//*4
System.out.println(future1.get());
//*5
Thread.sleep(3000);
System.out.println("Thread 2 terminated? :" + future2.cancel(true));
//*6
System.out.println(future3.get());
} catch (ExecutionException ex) {
ex.printStackTrace();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}