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

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

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

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

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

导致这个任务继续被挂起。

你可能感兴趣的:(Java 线程与并发研究系列四(多线程))