JAVA线程间通信:线程是操作系统中独立的个体,但是这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通信就成为整体的必用方式之一,当线程存在通信指挥,系统间的交互性会更加强大,在提高CPU利用率的同时还会使开发人员对线程任务在处理的过程中进行有效的把控与监督,
使用wait和notify方法实现线程间的通信。(注意这两个方法都是object方法,也就是java为所有的对象提供了这两个方法)。
1.wait和notify必须配合Synchronized关键字使用
2.wait方法释放锁,notify不释放锁
先看下例子:
账户类:
package com.ck.thread;
import java.math.BigDecimal;
public class Account {
private volatile BigDecimal balnace = new BigDecimal("0");
private Object obj = new Object();
//充值
public void cz() {
synchronized (obj) {
for(int i = 0; i<10; i++) {
balnace = balnace.add(new BigDecimal("10"));
if(balnace.compareTo(new BigDecimal("50")) == 0) {
obj.notify();
System.out.println("已经发出通知");
}
System.out.println("充值中.........");
}
}
}
//消费
public void xf() throws InterruptedException {
synchronized (obj) {
if(balnace.compareTo(new BigDecimal("50")) < 0) {
System.out.println("等待充值");
obj.wait();
System.out.println("余额已经充足,开始消费");
}
}
}
public BigDecimal getBalnace() {
return balnace;
}
public void setBalnace(BigDecimal balnace) {
this.balnace = balnace;
}
}
充值线程:
package com.ck.thread;
public class CzThread extends Thread{
private Account account;
public CzThread(Account account) {
this.account = account;
}
@Override
public void run() {
account.cz();
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
消费线程:
package com.ck.thread;
public class XfThread extends Thread{
private Account account;
public XfThread(Account account) {
super();
this.account = account;
}
@Override
public void run() {
try {
account.xf();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
}
主线程:
package com.ck.thread;
public class MainThread {
public static void main(String[] args) throws InterruptedException {
Account account = new Account();
XfThread xf = new XfThread(account);
xf.setName("xf");
CzThread cz = new CzThread(account);
cz.setName("cz");
xf.start();
cz.start();
}
}
看下运行结果:
等待充值
充值中.........
充值中.........
充值中.........
充值中.........
已经发出通知
充值中.........
充值中.........
充值中.........
充值中.........
充值中.........
充值中.........
余额已经充足,开始消费
从结果可以看出,xf线程调用wait方法后,释放了锁,而cz线程调用了notify继续往下执行了,说明并没有释放锁。这就是线程间最简单的通信方式,我们都知道本例中只是两个,如果是多于2个线程间怎么通信呢,这时候我们就要用到notifyAll,我们看下代码把上面的Notify缓存notifyAll:
package com.ck.thread;
import java.math.BigDecimal;
public class Account {
private volatile BigDecimal balnace = new BigDecimal("0");
private Object obj = new Object();
//充值
public void cz() {
synchronized (obj) {
for(int i = 0; i<10; i++) {
balnace = balnace.add(new BigDecimal("10"));
if(balnace.compareTo(new BigDecimal("50")) == 0) {
obj.notifyAll();
System.out.println("已经发出通知");
}
System.out.println("充值中.........");
}
}
}
//消费
public void xf() throws InterruptedException {
synchronized (obj) {
if(balnace.compareTo(new BigDecimal("50")) < 0) {
System.out.println("等待充值");
obj.wait();
System.out.println("余额已经充足,开始消费");
}
}
}
public BigDecimal getBalnace() {
return balnace;
}
public void setBalnace(BigDecimal balnace) {
this.balnace = balnace;
}
}
看下执行结果:
等待充值
充值中.........
充值中.........
充值中.........
充值中.........
已经发出通知
充值中.........
充值中.........
充值中.........
充值中.........
充值中.........
充值中.........
余额已经充足,开始消费
实际上跟上面的结果是一样的,因为这是两个线程间的通信,
notify和notifyAll都是把某个对象上休息区内的线程唤醒,notify只能唤醒一个,但究竟是哪一个不能确定,而notifyAll则唤醒这个对象上的休息室中所有的线程.
一般有为了安全性,我们在绝对多数时候应该使用notifiAll(),除非你明确知道只唤醒其中的一个线程.
锁池:
假设线程A已经拥有对象锁,线程B、C想要获取锁就会被阻塞,进入一个地方去等待锁的等待,这个地方就是该对象的锁池;
等待池:
假设线程A调用某个对象的wait方法,线程A就会释放该对象锁,同时线程A进入该对象的等待池中,进入等待池中的线程不会去竞争该对象的锁。
notify和notifyAll的区别:
1、notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会;
2、notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会;
一般如果不是一对一的线程通信,我们用notifyAll.