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

Java 线程与并发研究系列四(多线程)

2014-07-20 12:28 615 查看
当使用多个线程来同时运行多个任务时,有时候需要对某项共享资源进行操作,怎样使得一个任务不会干涉另外一个任务呢?这时候就需要

使用锁来使得资源的访问变得互斥,也就是同时只能有一个任务对共享资源进程访问。

Java中能够通过Object的wait()和notify()方法来安全的访问共享资源。Java SE5的并发库还提供了await()和signal()方法的Condition对象来实

现资源的安全访问。

下面我们来了解一下一些常用的线程同步相关方法

notify()和notifyAll()的不同,notify():在众多等待同一个锁的任务中,只有一个被唤醒,所以如果使用notify()时,就必须保证被唤醒的是恰当

的任务。notify一般用于具有唤醒同步块的对象。notifyAll()是唤醒所有正在等待同一个锁的任务。

wait()方法的作用是将调用该方法的线程挂起,和sleep(),yield()这些方法不同的是,在调用wait方法后,该对象上的锁会被释放掉,也就是

在此声明:我已经做完所有能做的事,因此我在这里等待,但是我希望其他的synchronized操作在条件适合的情况下能够执行。

wait,notify,notifyAll,这些方法的是基类Object的一部分,而不是属于Thread的一部分,我们可能会感到奇怪,为什么针对线程的功能却

作为基类的一部分来实现,这是因为这些操作的锁也是所有对象的一部分,所以你可以在任何的同步控制方法或者同步控制块中调用wait()

notify(),notifyAll(),如果在非同步控制方法中调用这些方法时,程序能够编译通过,但运行时会抛出IllegalMonitorStateException异常,也

就是在调用这些方法的任务在调用这些方法前必须拥有对象锁。

下面我们通过一个例子来熟悉这些方法的使用。

现在有一个场景是:一辆车,需要多次进行涂蜡和抛光,在涂蜡之前必须进行抛光,在抛光之前必须进行涂蜡,刚开始肯定是从涂蜡开始

然后进行抛光,然后再涂蜡,以此循环。下面给出这个场景的代码:

public class MyTest{

public static volatile boolean isStop = false;
public static void main(String[] args) throws InterruptedException{
Car car = new Car();
ExecutorService exec = Executors.newCachedThreadPool();
exec.submit(new Wax(car));
exec.submit(new Buff(car));
TimeUnit.SECONDS.sleep(5);
isStop = true;
exec.shutdownNow();
}

}

class Car{
private boolean waxOn= false;//涂蜡的标志位,ture表示正在涂蜡

/**
* 涂蜡*/
public synchronized void wax(){
waxOn = true;
notifyAll();
}

/**
* 抛光*/
public synchronized void buff(){
waxOn = false;
notifyAll();
}

/**
* 等待涂蜡*/
public synchronized void waitForWax()throws InterruptedException{
while(waxOn == false){
wait();
}
}
/**
* 等待抛光*/
public synchronized void waitForBuff()throws InterruptedException{
while(waxOn == true){
wait();
}
}
}

class Wax implements Runnable{
Car car;
public Wax(Car car){
this.car = car;
}
@Override
public void run() {
while(!MyTest.isStop){
try {
car.waitForBuff();

TimeUnit.MILLISECONDS.sleep(200);//涂蜡所需时间
System.out.println("正在涂蜡");
car.wax();
} catch (InterruptedException e) {
System.out.println("涂蜡完毕");
}
}
System.out.println("结束涂蜡");
}
}
class Buff implements Runnable{
Car car;
public Buff(Car car){
this.car = car;
}

@Override
public void run() {
while(!MyTest.isStop){
try {
car.waitForWax();
TimeUnit.MILLISECONDS.sleep(200);//抛光所需时间
System.out.println("正在抛光");
car.buff();
} catch (InterruptedException e) {
System.out.println("抛光完毕");
}
}
System.out.println("结束抛光");
}
}


上面的代码我们就实现了这个场景,最先开始提交涂蜡的任务,然后提交抛光的任务,确保任务是从涂蜡开始的。waxOn这个变量就是涂蜡

和抛光的标志为false时表示正在进行抛光,等待涂蜡,为true时表示正在进行涂蜡,等待抛光。涂蜡时,先调用waitForBuff()方法将抛光的线

程挂起,然后调用wax()方法进行涂蜡,在这个方法中将waxOn标志位设为true,涂蜡完毕后使用notifyAll()唤醒正在等待这辆车的抛光的任务。

在抛光时,先调用waitForWax()方法将涂蜡的线程挂起,然后调用buff()方法进行抛光,并将waxOn标志位设为false,抛光完毕后使用notifyAll()

方法唤醒正在等待这辆车的涂蜡任务。

让这个过程持续5秒,然后调用shutdownNow(),这个方法会调用由他控制的线程的interrupt()方法。至于为什么要使用一个while循环包围wait

方法,这是因为,可能由于其他原因,还有其他任务也在等待这辆车的同一个锁,而这个锁被唤醒时,可能会导致其他任务获取这个锁,从而

导致这个任务继续被挂起。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐