Java多线程发展简史(五)

2014-11-24 09:39:52 · 作者: · 浏览: 4
}
synchronized (thread) {
System.out.println("Has sleep() released the lock!");
thread.notify();
}
}
}
在这个JDK版本中,引入线程变量ThreadLocal这个类:

每一个线程都挂载了一个ThreadLocalMap。ThreadLocal这个类的使用很有意思,get方法没有key传入,原因就在于这个key就是当前你使用的这个ThreadLocal它自己。ThreadLocal的对象生命周期可以伴随着整个线程的生命周期。因此,倘若在线程变量里存放持续增长的对象(最常见是一个不受良好管理的map),很容易导致内存泄露。

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");

}
}

class User {

private static ThreadLocal enclosure = new ThreadLocal(); // is it must be static

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

public Object get() {
return enclosure.get();
}
}
上面的例子会一直打印var1,而不会打印var2,就是因为不同线程中的ThreadLocal是互相独立的。
用jstack工具可以找到锁相关的信息,如果线程占有锁,但是由于执行到wait方法时处于wait状态暂时释放了锁,会打印waiting on的信息:

"Thread-0" prio=6 tid=0x02bc4400 nid=0xef44 in Object.wait() [0x02f0f000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x22a7c3b8> (a Wait)
at java.lang.Object.wait(Object.java:485)
at Wait.run(Wait.java:8)
- locked <0x22a7c3b8> (a Wait)
如果程序持续占有某个锁(例如sleep方法在sleep期间不会释放锁),会打印locked的信息:

"Thread-0" prio=6 tid=0x02baa800 nid=0x1ea4 waiting on condition [0x02f0f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Wait.run(Wait.java:8)
- locked <0x22a7c398> (a Wait)
而如果是线程希望进入某同步块,而在等待锁的释放,会打印waiting to的信息:

"main" prio=6 tid=0x00847400 nid=0xf984 waiting for monitor entry [0x0092f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at Wait.main(Wait.java:23)
- waiting to lock <0x22a7c398> (a Wait)
JDK 1.4
在2002年4月发布的JDK1.4中,正式引入了NIO。JDK在原有标准IO的基础上,提供了一组多路复用IO的解决方案。
通过在一个Selector上挂接多个Channel,通过统一的轮询线程检测,每当有数据到达,触发监听事件,将事件分发出去,而不是让每一个channel长期消耗阻塞一个线程等待数据流到达。所以,只有在对资源争夺剧烈的高并发场景下,才能见到NIO的明显优势。

相较于面向流的传统方式这种面向块的访问方式会丢失一些简易性和灵活性。下面给出一个NIO接口读取文件的简单例子(仅示意用):

import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class NIO {

public static void nioRead(String file) throws IOException {
FileInputStream in = new FileInputStream(file);
FileChannel channel = in.getChannel();

ByteBuffer buffer = ByteBuffer.allocate(1024);
channel.read(buffer);
byte[] b = buffer.array();
System.out.println(new String(b));
channel.close();
}
}
JDK 5.0
2004年9月起JDK 1.5发布,并正式更名到5.0。有个笑话说,软件行业有句话,叫做“不要用3.0版本以下的软件”,意思是说版本太小的话往往软件质量不过关——但是按照这种说法,JDK的原有版本命名方式得要到啥时候才有3.0啊,于是1.4以后通过版本命名方式的改变直接升到5.0了。
JDK 5.0不只是版本号命名方式变更那么简单,对于多线程编程来说,这里发生了两个重大事件,JSR 133和JSR 166的正式发布。
JSR 133
JSR 133重新明确了Java内存模型,事实上,在这之前,常见的内存模型包括连续一致