Java多线程发展简史(1)

[size=medium]
[b]引言[/b]

首先问这样一个问题,如果提到Java多线程编程,你会想到什么?

volatile、synchronized关键字?
竞争和同步?
锁机制?
线程安全问题?
线程池和队列?
好吧,请原谅我在这里卖的关子,其实这些都对,但是又不足够全面,如果我们这样来谈论Java多线程会不会全面一些:

模型:JMM(Java内存模型)和JCM(Java并发模型)
使用:JDK中的并发包
实践:怎样写线程安全的代码
除错:使用工具来分析并发问题
……
可是,这未免太死板了,不是么?

不如换一个思路,我们少谈一些很容易查到的语法,不妨从历史的角度看看Java在多线程编程方面是怎样进化的,这个过程中,它做了哪些正确的决定,犯了哪些错误,未来又会有怎样的发展趋势?

另外,还有一点要说是,我希望通过大量的实例代码来说明这些事情。Linus说:“Talk is cheap, show me the code.”。下文涉及到的代码我已经上传,可以在此打包下载。

[b]诞生[/b]

Java的基因来自于1990年12月Sun公司的一个内部项目,目标设备正是家用电器,但是C++的可移植性和API的易用性都让程序员反感。旨在解决这样的问题,于是又了Java的前身Oak语言,但是知道1995年3月,它正式更名为Java,才算Java语言真正的诞生。

[b]JDK 1.0[/b]

1996年1月的JDK1.0版本,从一开始就确立了Java最基础的线程模型,并且,这样的线程模型再后续的修修补补中,并未发生实质性的变更,可以说是一个具有传承性的良好设计。

抢占式和协作式是两种常见的进程/线程调度方式,操作系统非常适合使用抢占式方式来调度它的进程,它给不同的进程分配时间片,对于长期无响应的进程,它有能力剥夺它的资源,甚至将其强行停止(如果采用协作式的方式,需要进程自觉、主动地释放资源,也许就不知道需要等到什么时候了)。Java语言一开始就采用协作式的方式,并且在后面发展的过程中,逐步废弃掉了粗暴的stop/resume/suspend这样的方法,它们是违背协作式的不良设计,转而采用wait/notify/sleep这样的两边线程配合行动的方式。

一种线程间的通信方式是使用中断:
[/size]
package com.jiaozg.thread;

public class InterruptCheck extends Thread {

@Override
public void run() {
System.out.println("start");
while(true) {
if(Thread.currentThread().isInterrupted()) {
break;
}
}
System.out.println("while exit");
}

public static void main(String[] args) {
Thread thread = new InterruptCheck();
thread.start();
try {
sleep(2000);
} catch (InterruptedException e) {
}
thread.interrupt();
}
}

[size=medium]这是中断的一种使用方式,看起来就像是一个标志位,线程A设置这个标志位,线程B时不时地检查这个标志位。另外还有一种使用中断通信的方式,如下:[/size]
package com.jiaozg.thread;

public class InterruptWait extends Thread {

public static Object lock = new Object();

@Override
public void run() {
System.out.println("start");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt(); // set interrupt flag again
System.out.println(Thread.currentThread().isInterrupted());
e.printStackTrace();
}
}
}

public static void main(String[] args) {
Thread thread = new InterruptWait();
thread.start();
try {
sleep(2000);
} catch (InterruptedException e) {
}
thread.interrupt();
}

}

[size=medium]在这种方式下,如果使用wait方法处于等待中的线程,被另一个线程使用中断唤醒,于是抛出InterruptedException,同时,中断标志清除,这时候我们通常会在捕获该异常的地方重新设置中断,以便后续的逻辑通过检查中断状态来了解该线程是如何结束的。

在比较稳定的JDK 1.0.2版本中,已经可以找到Thread和ThreadUsage这样的类,这也是线程模型中最核心的两个类。整个版本只包含了这样几个包:java.io、 java.util、java.net、java.awt和java.applet,所以说Java从一开始这个非常原始的版本就确立了一个持久的线程模型。

值得一提的是,在这个版本中,原子对象AtomicityXXX已经设计好了,这里给出一个例子,说明i++这种操作时非原子的,而使用原子对象可以保证++操作的原子性:[/size]
package com.jiaozg.thread;

import java.util.concurrent.atomic.AtomicInteger;

public class Atomicity {

private static volatile int nonAtomicCounter = 0;
private static volatile AtomicInteger atomicCounter = new AtomicInteger(0);

private static int times = 0;

public static void caculate() {
times++;
for (int i = 0; i < 1000; i++) {
new Thread(new Runnable() {
public void run() {
nonAtomicCounter++;
atomicCounter.incrementAndGet();
}
}).start();
}

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

public static void main(String[] args) {
caculate();
while (nonAtomicCounter == 1000) {
nonAtomicCounter = 0;
atomicCounter.set(0);
caculate();
}

System.out.println("Non-atomic counter: " + times + ":"
+ nonAtomicCounter);
System.out.println("Atomic counter: " + times + ":" + atomicCounter);
}

}

[size=medium]上面这个例子你也许需要跑几次才能看到效果,使用非原子性的++操作,结果经常小于1000。

对于锁的使用,网上可以找到各种说明,但表述都不够清晰。请看下面的代码:[/size]
package com.jiaozg.thread;

public class Lock {

private static Object o = new Object();
static Lock lock = new Lock();

// lock on dynamic method
public synchronized void dynamicMethod() {
System.out.println("dynamic method");
sleepSilently(2000);
}

// lock on static method
public static synchronized void staticMethod() {
System.out.println("static method");
sleepSilently(2000);
}

// lock on this
public void thisBlock() {
synchronized (this) {
System.out.println("this block");
sleepSilently(2000);
}
}

// lock on an object
public void objectBlock() {
synchronized (o) {
System.out.println("dynamic block");
sleepSilently(2000);
}
}

// lock on the class
public static void classBlock() {
synchronized (Lock.class) {
System.out.println("static block");
sleepSilently(2000);
}
}

private static void sleepSilently(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}

public static void main(String[] args) {

// object lock test
new Thread() {
@Override
public void run() {
lock.dynamicMethod();
}
}.start();
new Thread() {
@Override
public void run() {
lock.thisBlock();
}
}.start();
new Thread() {
@Override
public void run() {
lock.objectBlock();
}
}.start();

sleepSilently(3000);
System.out.println();

// class lock test
new Thread() {
@Override
public void run() {
lock.staticMethod();
}
}.start();
new Thread() {
@Override
public void run() {
lock.classBlock();
}
}.start();

}

}

[size=medium]上面的例子可以反映对一个锁竞争的现象,结合上面的例子,理解下面这两条,就可以很容易理解synchronized关键字的使用:

非静态方法使用synchronized修饰,相当于synchronized(this)。
静态方法使用synchronized修饰,相当于synchronized(Lock.class)。[/size]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值