多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
虽然【线程同步】中的案例2实现了线程同步,但是我们发现不能人为干涉,即我们不能让两个线程按照我们想要的规则来运行,案例2每次运行结果都可能是不一样的。 <br/> 如果需要人为控制线程的运行规则,就需要考虑到线程之间的通信问题了。 <br/> 如果我们想控制或改变CPU切换线程的随机性,可以通过Java的等待唤醒机制,让多个线程依靠同一个同步锁,借助`wait`和`notify`方法来实现线程之间的协调通信。 <br/> <mark>多个线程必须使用同一个锁,才能互相通信</mark>。 ```java public class Object { public final void wait() // 让当前的线程释放锁 public final native void wait(long timeout) // 让当前的线程在timeout毫米后释放锁 public final native void notify() // 唤醒持有同一个锁的某个线程 public final native void notifyAll() // 唤醒持有统一锁的所有线程 ... } ``` 注意:`wait`和`notify`都是当前线程已经持有锁的时候,才可以调用,否则抛出异常。 <br/> **案例3:农夫摘水果问题** 一个农夫摘水果放到果篮中,一个小孩从果篮中拿水果吃。 <br/> 要求实现:如果果篮装满了,农夫停;如果果篮是空的,小孩停;如果果篮既没有满也没有空,则农夫和小孩不得休息。 <br/> 假设:农夫是一个线程、小孩是一个线程、果篮就是这两个线程的锁。 ```java public class TestSyn { //果篮就是一个锁 public static List<String> baseket = new ArrayList<>(); /** * 农夫线程 */ class FarmerSyn implements Runnable { @Override public void run() { for (int i = 0; i < 30; i++) { //同步块 synchronized (baseket) { //果篮已满 if (baseket.size() >= 5) { try { System.out.println("果篮满了,农夫休息."); baseket.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //果篮未满 baseket.add("apple"); System.out.println("现在果篮中有" + baseket.size() + "个苹果."); baseket.notify(); } try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 小孩线程 */ class ChildrenSyn implements Runnable { @Override public void run() { for (int i = 0; i < 30; i++) { synchronized (baseket) { //果篮空了 if (baseket.size() == 0) { try { System.out.println("没有水果了,小孩停下."); baseket.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //果篮未空 baseket.remove("apple"); System.out.println("小孩吃了一个,还剩" + baseket.size() + "个苹果."); baseket.notify(); } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { TestSyn testSyn = new TestSyn(); Thread farmerThread = new Thread(testSyn.new FarmerSyn()); Thread childrenThread = new Thread(testSyn.new ChildrenSyn()); farmerThread.start(); childrenThread.start(); } } ``` 某一次运行的结果如下: ``` 现在果篮中有1个苹果. 小孩吃了一个,还剩0个苹果. 现在果篮中有1个苹果. 现在果篮中有2个苹果. 小孩吃了一个,还剩1个苹果. 现在果篮中有2个苹果. 现在果篮中有3个苹果. 小孩吃了一个,还剩2个苹果. ...... ``` 通过调整两个人吃或摘的速度,就能看到不同效果。 当你都将两个线程的`Thread.sleep(200)`调整成一样的时间,这两个线程则是轮着执行的,如下: ``` 现在果篮中有1个苹果. 小孩吃了一个,还剩0个苹果. 现在果篮中有1个苹果. 小孩吃了一个,还剩0个苹果. 现在果篮中有1个苹果. 小孩吃了一个,还剩0个苹果. 现在果篮中有1个苹果. ...... ``` 说明我们可以做到让这两个线程按照我们想要的来运行了。