线程间的通信

1,我还记得最开始写通信小程序的时候,那时候的数据传输就是用死循环来检测数据是否发生了变化,但是这种通信会浪费CPU资源,而且如果轮询的时间过大,有可能你还没取到你要的值,这个值可能就发生了更改,导致本来可以跳出的死循环跳不出来。因此引入了一种机制wait/notify。

2 wait/notify
wait() 方法是Object类的方法,它的作用是使当前执行wait()方法的线程等待,在wait() 所在的代码行处暂停执行,并释放锁,在调用wait()之前,线程必须获得该对象的对象级别的锁,即只能在同步方法或同步块中调用wait()方法。
notify() 方法要在同步方法或同步块中调用,即在调用前,线程必须获得锁。该方法用来通知那些可能等待该锁的其他线程,如果又多个线程等待,则按照执行wait()方法的顺序对处于wait()方法的顺序对处于wait状态的线程发出一次通知(notify) ,并使该线程重新获得锁。

使用方法如下

public class Wait_learning {
	
	public static void main(String[] args) {
		Object lock = new Object();
		Thread_AA a = new Thread_AA(lock);
		a.start();
		try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Thread_BB b = new Thread_BB(lock);
		b.start();
	}

}
class MyList{
	private static List list = new ArrayList();
	public static void add() {
		list.add("anyString");
		
	}
	public static int size() {
		return list.size();
	}
}
class Thread_AA extends Thread{
	private Object lock;
	public Thread_AA(Object lock) {
		super();
		this.lock = lock;
		
	}
	public void run() {
		try {
			synchronized(lock) {
				if(MyList.size() != 5) {
					System.out.println("wait begin " + System.currentTimeMillis());
					lock.wait();
					System.out.println("wait  end" + System.currentTimeMillis());
				}
			}
		}catch(InterruptedException ef) {
			ef.printStackTrace();
		}
		
	}
}
class Thread_BB extends Thread{
	private Object lock;
	public Thread_BB(Object lock) {
		super();
		this.lock = lock;
	}
	public void run() {
		synchronized(lock) {
			for(int i = 0; i < 10; i ++) {
				MyList.add();
				if(MyList.size() == 5) {
					lock.notify();
					System.out.println("已发出通知!");
				}
				System.out.println("添加了 " + (i + 1) + "个元素");
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

结果如下
在这里插入图片描述

wait()方法可以使调用该方法的线程释放锁,然后从运行状态转换成wait状态,等待被唤醒。
notify()方法按照执行wait()方法的顺序唤醒等待同一锁的一个线程,使其进入运行状态,即notify()方法仅通知一个线程
notifyAll()方法执行后,会按照执行wait()方法相反的顺序依次唤醒全部的线程。

3 生产者/消费者模式的实现
wait/notify最经典的案例就是生产者/消费者模式。
如下面的一对一模式

public class PC {
	private String a = " ";
	public synchronized void setValue() throws InterruptedException {
		if(!a.equals(" ")) this.wait();
		a = System.currentTimeMillis() + "_" + System.nanoTime();
		System.out.println("set的值是" + a);
		this.notify();
	}
	public synchronized void getValue() throws InterruptedException {
		if(a.equals(" ")) this.wait();
		System.out.println("get的值是" + a);
		a = " ";
		this.notify();
	}
	public static void main(String[] args) {
		PC p = new PC();
		String a = " ";
		Thread_1 thread_1 = new Thread_1(p);
		Thread_2 thread_2 = new Thread_2(p);
		thread_1.start();
		thread_2.start();
	}

}
class Thread_1 extends Thread{//线程1作为生产者
	private PC p;
	
	public Thread_1(PC p) {
		this.p = p;
		
	}
	public void run() {
		try {
			while(true)
			p.setValue();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
class Thread_2 extends Thread{//线程2作为消费者
	private PC p;
	
	public Thread_2(PC p) {
		this.p = p;
		
	}
	public void run() {
		try {
			while(true)
			p.getValue();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

运行结果如下
在这里插入图片描述
注意,由于锁的分配具有随机性,因此如果用上面的例子实现多生产和多消费很容易出现假死的现象,即整个项目处于停止状态。这是因为由于锁分配的随机性,锁从一个生产者转移到另一个生产者上,或者从一个消费者转移到另一个消费者上,导致一个生产者或消费者,平白无故的运行了wait()代码。随着时间推移,这种情况的出现越来越多,最后整个程序中的线程全部变成了wait()状态。程序也就停止运行了。这个时候使用notifyAll()就可以避免这个情况。
while结合notifyAll()来实现任意数量的几对几生产与消费的方法具有通用性。

4 join()方法
join()方法的作用是使所属的线程对象x正常执行run()方法中的任务,而使当前线程z进行无限期阻塞。知道线程对象x结束运行。
其实就相当于在线程z里面放了个死循环while(x.isAlive)而已,跟wait()没什么关系。但是如果你有同步方法的话,这个wait()才会生效。以防止死锁的产生。 吧 不不不
看源代码就知道了

 public final void join() throws InterruptedException {
        join(0);
    }
public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);//wait() 就是 wait(0)
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

使用方法如下

public class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		try {
			MyThread_2 threadTest = new MyThread_2();
			threadTest.start();
			threadTest.join();
			System.out.println("我想当threadTest对象执行完毕后我再执行");
			
			
		}catch(InterruptedException ef) {
			ef.printStackTrace();
		}
	}

}
class MyThread_2 extends Thread{
	public void run() {
		int value = (int) (Math.random()*1000);
		System.out.println(value);
		try {
			Thread.sleep(value);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	}
}

运行结果如下
在这里插入图片描述

此外,join(Long millins)用法和wait(Long millis)没什么区别。

5 ThreadLocal类的使用
即Map表的使用只不过key值为对应所在线程的threadLocal对象,value值为存储的数据。用来确保线程间的变量是独立的。
此处不详述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值