您的位置:首页 > 编程语言 > Java开发

Java多线程2-线程协作、Timer和TimerTask

2013-06-17 14:51 387 查看
/article/2581122.html

线程协作

生产者/消费者模式是一个经典的线程同步以及通信的模型。

假设有这样一种情况,有一个盘子,盘子里只能放一个鸡蛋,A线程专门往盘子里放鸡蛋,如果盘子里有鸡蛋,则一直等到盘子里没鸡蛋,B线程专门从盘子里取鸡蛋,如果盘子里没鸡蛋,则一直等到盘子里有鸡蛋。这里盘子是一个互斥区,每次放鸡蛋是互斥的,每次取鸡蛋也是互斥的,A线程放鸡蛋,如果这时B线程要取鸡蛋,由于A没有释放锁,B线程处于等待状态,进入阻塞队列,放鸡蛋之后,要通知B线程取鸡蛋,B线程进入就绪队列,反过来,B线程取鸡蛋,如果A线程要放鸡蛋,由于B线程没有释放锁,A线程处于等待状态,进入阻塞队列,取鸡蛋之后,要通知A线程放鸡蛋,A线程进入就绪队列。我们希望当盘子里有鸡蛋时,A线程阻塞,B线程就绪,盘子里没鸡蛋时,A线程就绪,B线程阻塞,代码如下:

[java] view
plaincopyprint?

import java.util.ArrayList;

import java.util.List;

/** 定义一个盘子类,可以放鸡蛋和取鸡蛋 */

public class Plate {

/** 装鸡蛋的盘子 */

List<Object> eggs = new ArrayList<Object>();

/** 取鸡蛋 */

public synchronized Object getEgg() {

while (eggs.size() == 0) {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

Object egg = eggs.get(0);

eggs.clear();// 清空盘子

notify();// 唤醒阻塞队列的某线程到就绪队列

System.out.println("拿到鸡蛋");

return egg;

}

/** 放鸡蛋 */

public synchronized void putEgg(Object egg) {

while (eggs.size() > 0) {

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

eggs.add(egg);// 往盘子里放鸡蛋

notify();// 唤醒阻塞队列的某线程到就绪队列

System.out.println("放入鸡蛋");

}

static class AddThread extends Thread {

private Plate plate;

private Object egg = new Object();

public AddThread(Plate plate) {

this.plate = plate;

}

public void run() {

plate.putEgg(egg);

}

}

static class GetThread extends Thread {

private Plate plate;

public GetThread(Plate plate) {

this.plate = plate;

}

public void run() {

plate.getEgg();

}

}

public static void main(String args[]) {

Plate plate = new Plate();

for(int i = 0; i < 10; i++) {

new Thread(new AddThread(plate)).start();

new Thread(new GetThread(plate)).start();

}

}

}

输出结果:

[java] view
plaincopyprint?

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

放入鸡蛋

拿到鸡蛋

8 l程序开始,A线程判断盘子是否为空,放入一个鸡蛋,并且唤醒在阻塞队列的一个线程,阻塞队列为空;假设CPU又调度了一个A线程,盘子非空,执行等待,这个A线程进入阻塞队列;然后一个B线程执行,盘子非空,取走鸡蛋,并唤醒阻塞队列的A线程,A线程进入就绪队列,此时就绪队列就一个A线程,马上执行,放入鸡蛋;如果再来A线程重复第一步,在来B线程重复第二步,整个过程就是生产者(A线程)生产鸡蛋,消费者(B线程)消费鸡蛋。

下面讲述了一个线程通信的,在此也分享一下。

题目:子线程循环10次,主线程循环100次,如此循环100次。

[java] view
plaincopyprint?

public class ThreadTest2 {

public static void main(String[] args) {

final Business business = new Business();

new Thread(new Runnable() {

@Override

public void run() {

threadExecute(business, "sub");

}

}).start();

threadExecute(business, "main");

}

public static void threadExecute(Business business, String threadType) {

for(int i = 0; i < 100; i++) {

try {

if("main".equals(threadType)) {

business.main(i);

} else {

business.sub(i);

}

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

class Business {

private boolean bool = true;

public synchronized void main(int loop) throws InterruptedException {

while(bool) {

this.wait();

}

for(int i = 0; i < 100; i++) {

System.out.println("main thread seq of " + i + ", loop of " + loop);

}

bool = true;

this.notify();

}

public synchronized void sub(int loop) throws InterruptedException {

while(!bool) {

this.wait();

}

for(int i = 0; i < 10; i++) {

System.out.println("sub thread seq of " + i + ", loop of " + loop);

}

bool = false;

this.notify();

}

}

大家注意到没有,在调用wait方法时,都是用while判断条件的,而不是if,在wait方法说明中,也推荐使用while,因为在某些特定的情况下,线程有可能被假唤醒,使用while会循环检测更稳妥。

Timer和TimerTask可以做为实现线程的第三种方式,前两中方式分别是继承自Thread类和实现Runnable接口。

Timer是一种线程设施,用于安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行,可以看成一个定时器,可以调度TimerTask。TimerTask是一个抽象类,实现了Runnable接口,所以具备了多线程的能力。

一个Timer可以调度任意多个TimerTask,它会将TimerTask存储在一个队列中,顺序调度,如果想两个TimerTask并发执行,则需要创建两个Timer。下面来看一个简单的例子:

[java] view
plaincopyprint?

import java.util.Timer;

import java.util.TimerTask;

public class TimerTest {

static class MyTimerTask1 extends TimerTask {

public void run() {

System.out.println("爆炸!!!");

}

}

public static void main(String[] args) {

Timer timer = new Timer();

timer.schedule(new MyTimerTask1(), 2000);// 两秒后启动任务

}

}

schedule是Timer调度任务的方法,Timer重构了四个schedule方法,具体可以查看JDK API。

看一个稍复杂的例子,假设有这样一种需求,实现一个连环炸弹,2秒后爆炸一次,3秒后爆炸一次,如此循环下去,这就需要创建两个任务,互相调度,代码如下:

[java] view
plaincopyprint?

import java.util.Date;

import java.util.Timer;

import java.util.TimerTask;

public class TimerTest {

static class MyTimerTask1 extends TimerTask {

public void run() {

System.out.println("爆炸!!!");

new Timer().schedule(new MyTimerTask2(), 2000);

}

}

static class MyTimerTask2 extends TimerTask {

public void run() {

System.out.println("爆炸!!!");

new Timer().schedule(new MyTimerTask1(), 3000);

}

}

public static void main(String[] args) {

Timer timer = new Timer();

timer.schedule(new MyTimerTask2(), 2000);

while(true) {

System.out.println(new Date().getSeconds());

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: