Java多线程发展简史(2)

[size=medium][b]JDK 1.2[/b]

1998年年底的JDK1.2版本正式把Java划分为J2EE/J2SE/J2ME三个不同方向。在这个版本中,Java试图用Swing修正在 AWT中犯的错误,例如使用了太多的同步。可惜的是,Java本身决定了AWT还是Swing性能和响应都难以令人满意,这也是Java桌面应用难以比及其服务端应用的一个原因,在IBM后来的SWT,也不足以令人满意,JDK在这方面到JDK 1.2后似乎反省了自己,停下脚步了。值得注意的是,JDK高版本修复低版本问题的时候,通常遵循这样的原则:

向下兼容。所以往往能看到很多重新设计的新增的包和类,还能看到deprecated的类和方法,但是它们并不能轻易被删除。
严格遵循JLS(Java Language Specification),并把通过的新JSR(Java Specification Request)补充到JLS中,因此这个文档本身也是向下兼容的,后面的版本只能进一步说明和特性增强,对于一些最初扩展性比较差的设计,也会无能为力。这个在下文中关于ReentrantLock的介绍中也可以看到。
在这个版本中,正式废除了这样三个方法:stop()、suspend()和resume()。下面我就来介绍一下,为什么它们要被废除:[/size]

package com.jiaozg.thread;

public class Stop extends Thread {

@Override
public void run() {
try {
while (true)
;
} catch (Throwable e) {
e.printStackTrace();
}
}

public static void main(String[] args) {
Thread thread = new Stop();
thread.start();

try {
sleep(1000);
} catch (InterruptedException e) {
}

thread.stop(new Exception("stop")); // note the stack trace
}

}

[size=medium]从上面的代码你应该可以看出两件事情:

使用stop来终止一个线程是不讲道理、极其残暴的,不论目标线程在执行任何语句,一律强行终止线程,最终将导致一些残缺的对象和不可预期的问题产生。
被终止的线程没有任何异常抛出,你在线程终止后找不到任何被终止时执行的代码行,或者是堆栈信息(上面代码打印的异常仅仅是main线程执行stop语句的异常而已,并非被终止的线程)。
很难想象这样的设计出自一个连指针都被废掉的类型安全的编程语言,对不对?再来看看suspend的使用,有引起死锁的隐患:[/size]
package com.jiaozg.thread;

public class Suspend extends Thread {

@Override
public void run() {
synchronized (this) {
while (true)
;
}
}

public static void main(String[] args) {
Thread thread = new Suspend();
thread.start();

try {
sleep(1000);
} catch (InterruptedException e) {
}

thread.suspend();

synchronized (thread) { // dead lock
System.out.println("got the lock");
thread.resume();
}
}

}

[size=medium]从上面的代码可以看出,Suspend线程被挂起时,依然占有锁,而当main线程期望去获取该线程来唤醒它时,彻底瘫痪了。由于suspend在这里是无期限限制的,这会变成一个彻彻底底的死锁。

相反,看看这三个方法的改进品和替代品:wait()、notify()和sleep(),它们令线程之间的交互就友好得多:[/size]
package com.jiaozg.thread;

public class Wait extends Thread {

@Override
public void run() {
System.out.println("start");
synchronized (this) { // wait/notify/notifyAll use the same
// synchronization resource
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace(); // notify won't throw exception
}
}
}

public static void main(String[] args) {
Thread thread = new Wait();
thread.start();
try {
sleep(2000);
} catch (InterruptedException e) {
}
synchronized (thread) {
System.out.println("Wait() will release the lock!");
thread.notify();
}
}

}

[size=medium]在wait和notify搭配使用的过程中,注意需要把它们锁定到同一个资源上(例如对象a),即:

一个线程中synchronized(a),并在同步块中执行a.wait()
另一个线程中synchronized(a),并在同步块中执行a.notify()
再来看一看sleep方法的使用,回答下面两个问题:

和wait比较一下,为什么sleep被设计为Thread的一个静态方法(即只让当前线程sleep)?
为什么sleep必须要传入一个时间参数,而不允许不限期地sleep?
如果我前面说的你都理解了,你应该能回答这两个问题。[/size]
package com.jiaozg.thread;

public class Sleep extends Thread {

@Override
public void run() {
System.out.println("start");
synchronized (this) { // sleep() can use (or not) any synchronization resource
try {
/**
* Do you know: <br>
* 1. Why sleep() is designed as a static method comparing with
* wait?<br>
* 2. Why sleep() must have a timeout parameter?
*/
this.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace(); // notify won't throw exception
}
}
}


public static void main(String[] args) {
Thread thread = new Sleep();
thread.start();
try {
sleep(2000);
} catch (InterruptedException e) {
}
synchronized (thread) {
System.out.println("Has sleep() released the lock!");
thread.notify();
}
}

}

[size=medium]在这个JDK版本中,引入线程变量ThreadLocal这个类:[/size]

[img]http://dl.iteye.com/upload/attachment/0074/2698/76c1ef6c-c88a-3ab8-b6aa-c54f5a4c8651.png[/img]
[size=medium]每一个线程都挂载了一个ThreadLocalMap。ThreadLocal这个类的使用很有意思,get方法没有key传入,原因就在于这个 key就是当前你使用的这个ThreadLocal它自己。ThreadLocal的对象生命周期可以伴随着整个线程的生命周期。因此,倘若在线程变量里存放持续增长的对象(最常见是一个不受良好管理的map),很容易导致内存泄露。[/size]
package com.jiaozg.thread;

public class ThreadLocalUsage extends Thread{

public User user = new User();

public User getUser() {
return user;
}

@Override
public void run() {
this.user.set("var1");

while (true) {
try {
sleep(1000);
} catch (InterruptedException e) {
}
System.out.println(this.user.get());
}
}

public static void main(String[] args) {

ThreadLocalUsage thread = new ThreadLocalUsage();
thread.start();

try {
sleep(4000);
} catch (InterruptedException e) {
}

thread.user.set("var2");

}
}

package com.jiaozg.thread;

public class User {

private static ThreadLocal<Object> enclosure = new ThreadLocal<Object>(); // is it must be static?

public void set(Object object) {
enclosure.set(object);
}

public Object get() {
return enclosure.get();
}

}

[size=medium]上面的例子会一直打印var1,而不会打印var2,就是因为不同线程中的ThreadLocal是互相独立的。[/size]

ref:http://developer.51cto.com/art/201209/357617_1.htm
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值