Java基础14--多线程(一)

2014-11-24 07:36:58 · 作者: · 浏览: 1

14-1,多线程-线程间通信示例

1,什么是多线程间通讯?

多线程通讯:多个线程在处理同一个资源,但是任务不同。

比如说:有一堆煤(资源),一辆卡车向里放煤(Input),一辆卡车向外取煤(output),放煤和取煤的任务不同,但操作的是同一个资源。

由于有两个任务,所以要定义两个run方法。

2,以下面代码说明。

定义name和sex变量,实现交替输出不同信息的功能。定义两个run方法,一个输入名字,另一个打印名字,因为name和sex是资源,将之封装成一个类,两个run方法分别创建一个类,代码如下:

//资源
class Resource {
	String name;
	String sex;
}
class Input implements Runnable {
	Resource r;
	//资源是一开始就有的,定义在构造器中
	Input(Resource r) {
		this.r = r;
	}
	public void run() {
		int x = 0;
		while(true) {
			//这里会出现安全问题
			synchronized(r) {
				if(x == 0) {
					r.name = "mike";
					r.sex = "nan";
				} else {
					r.name = "丽丽";
					r.sex = "女女女女女";
				}
			}
			//实现x在0和1之间的变化,保证两个赋值都能执行到
			x = (x + 1) % 2;
		}
	}
}
class Output implements Runnable {
	Resource r;
	Output(Resource r) {
		this.r = r;
	}
	public void run() {
		while(true) {
			synchronized(r) {
				//这句是把赋的值输出出来,跟赋值有关,也应该放在同步代码块中
				System.out.println(r.name + "..." + r.sex);
			}
		}
	}
}
class ResourceDemo {
	public static void main(String[] args) {
		//创建资源
		/*
		创建资源,并用Input和Output传入进去,保证两个run操作的是同一个Resource对象。
		并且保证了两个线程用的是同一个锁。
		*/
		Resource r = new Resource();
		//创建任务
		Input in = new Input(r);
		Output out = new Output(r);
		//创建线程
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(out);
		//开启线程
		t1.start();
		t2.start();
	}
}

说明:不加同步时可能会出现mike...女女女女女,或,丽丽...nan的情况,因为在进行完一次赋值后,切换到另一种赋值时,如赋了mike...nan,在要赋丽丽,女女女女女时,CPU在只赋了丽丽后,就切走了,这时sex还是nan,所以出现了t2输出丽丽...nan的情况,加入同步代码块就能解决。

锁的问题:如果两个线程不用同一个锁是不能解决问题的,所以在main中创建一个Resource对象,把r传入,这样两个线程就能用同一个锁了。

14-2,线程间通信,等待唤醒机制

1,上一个例子中,我们希望,输出完mike...nan后就输出丽丽...女女女女女,再输出mike...nan这样。

2,等待,唤醒机制

涉及的方法:

(1)wait():让线程处于冻结状态,被wait的线程会被存储在线程池中。

(2)notify():唤醒线程池中的一个线程(任意)。

(3)notifyAll():唤醒线程池中的所有线程。

这些方法都必须被定义在同步中,因为这些方法是用于操作线程状态的方法,必须要明确操作的是哪个锁上的线程。

3,为什么操作线程的方法wait,notify,notifyAll定义在了Object中?

因为这些方法是监视器的方法(锁),监视器其实就是锁,锁可以是任意对象,任意对象调用的方法一定定义在Object中。

4,以代码说明14-1中程序实现1中的需求。

//资源
class Resource {
	String name;
	String sex;
	//用于判断name和sex的值是否为空
	boolean flag = false;
}
class Input implements Runnable {
	Resource r;
	//资源是一开始就有的,定义在构造器中
	Input(Resource r) {
		this.r = r;
	}
	public void run() {
		int x = 0;
		while(true) {
			//这里会出现安全问题
			synchronized(r) {
				//第一次为false,不执行
				if(r.flag) {
					try{
						r.wait();
					} catch(InterruptedException e) {}
				}
				if(x == 0) {
					r.name = "mike";
					r.sex = "nan";
				} else {
					r.name = "丽丽";
					r.sex = "女女女女女";
				}
				//name和sex已经赋值,不为空,置为true,t2可以从中取值输出
				r.flag = true;
				//唤醒t2线程
				r.notify();
			}
			//实现x在0和1之间的变化,保证两个赋值都能执行到
			x = (x + 1) % 2;
		}
	}
}
class Output implements Runnable {
	Resource r;
	Output(Resource r) {
		this.r = r;
	}
	public void run() {
		while(true) {
			synchronized(r) {
				//flag已经是true,!flag是false,第一次不执行
				if(!r.flag) {
					try{
						r.wait();
					}catch(InterruptedException e) {}
				}
				//这句是把赋的值输出出来,跟赋值有关,也应该放在同步代码块中
				System.out.println(r.name + "..." + r.sex);
				//输出一次把flag置为flase,防止继续输出mike...nan,
				//因为若t2继续拿着执行权,!flag为true,t2会被wait。
				r.flag = false;
				r.notify();
			}
		}
	}
}
class ResourceDemo {
	public static void main(String[] args) {
		//创建资源
		/*
		创建资源,并用Input和Output传入进去,保证两个run操作的是同一个Resource对