1. 线程通信
JDK1.5之前有三个方法实现线程通信:
wait() :线程执行完这个方法进入等待队列(一个锁对应一个等待队列),需要被唤醒
notify():通知唤醒,从等待队列中随机唤醒一个线程
notifyAll():全部唤醒
(例)共用一张银行卡,保证存一笔,再取一笔
class demo {
public static void main(String[] args) {
Bankcard card=new Bankcard();
Addmoney addmoney=new Addmoney(card);
Submoney submoney=new Submoney(card);
new Thread(addmoney,"小张").start();
new Thread(submoney,"小美").start();
}
}
class Bankcard{
private double money;
private boolean falg;
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public synchronized void save(){
while (falg){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
money=money+1000;
System.out.println(Thread.currentThread().getName() + "存了1000,余额" + money);
falg=true;
this.notifyAll();
}
public synchronized void take(){
while (falg==false){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
money=money-1000;
System.out.println(Thread.currentThread().getName() + "取了1000,余额" + money);
falg=false;
this.notifyAll();
}
}
class Addmoney implements Runnable{
private Bankcard card;
public Addmoney(Bankcard card) {
this.card = card;
}
@Override
public void run() {
for (int i=0;i<10;i++) {
card.save();
}
}
}
class Submoney implements Runnable{
private Bankcard card;
public Submoney(Bankcard card) {
this.card = card;
}
@Override
public void run() {
for (int i=0;i<10;i++) {
card.take();
}
}
}
2. 线程的一些问题
(1)四窗口卖票重复问题
w1抢到cpu时间片,卖第100张票,休眠释放cpu,没有执行ticket--
w2抢到cpu时间片,这时ticket还是100,所以卖票重复
解决方法:上锁 synchronized 或者用Lock
(2)四窗口卖票,负票问题
w1抢到cpu时间片,卖第100张票,休眠释放cpu,没有执行ticket--
w2抢到cpu时间片,卖第100张票,休眠释放cpu,没有执行ticket--
这时,w1醒来,执行ticke--,票为0
然后,w2醒来,执行ticker--,票为-1
解决方法:在锁里面再判断
(3)两人存钱两人取钱一存一取,多存多取问题
假设1号、2号为存;3号、4号为取
1号判断有钱,等待,释放cpu
3号取钱,释放cpu
2号判断没钱,执行存钱,此时,1号醒来,也执行存钱,所以造成多存问题
解决方法:把 if() 判断改为while() 循环判断,可达到当它醒来再次判断的效果
(4)两人存钱两人取钱一存一取,唤醒错误(都等待)问题
1号抢到cpu,存钱成功,修改标记true,唤醒 小黑屋:0人
2号抢到cpu,判断有钱,等待,释放cpu 小黑屋:2号
1号抢到cpu,判断有钱,等待,释放cpu 小黑屋:2号、1号
3号抢到cpu,取钱成功,修改标记false,唤醒1号 小黑屋:2号
3号抢到cpu,判断没钱,等待,释放cpu 小黑屋:2号、3号
4号抢到cpu,判断没钱,等待,释放cpu 小黑屋:2号、3号、4号
1号抢到cpu,存钱成功,修改标记true,唤醒2号 小黑屋:3号、4号
1号抢到cpu,判断有钱,等待,释放cpu 小黑屋:3号、4号、1号
2号抢到cpu,判断有钱,等待,释放cpu 小黑屋:3号、4号、1号、2号
解决方法:把 notify() 唤醒方法修改为 notifyAll()
3. 生产者与消费者设计模式原理
指有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者可以从仓库取走产品
class demo {
public static void main(String[] args) {
breadContainer b=new breadContainer();
Producer producer = new Producer(b);
Consumer consumer=new Consumer(b);
Thread t1 = new Thread(producer,"小张");
Thread t2 = new Thread(consumer,"小美");
t1.start();
t2.start();
}
}
class bread{
private int id;
private String produceName;
public bread(int id, String produceName) {
this.id = id;
this.produceName = produceName;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getProduceName() {
return produceName;
}
public void setProduceName(String produceName) {
this.produceName = produceName;
}
@Override
public String toString() {
return "bread{" +
"id=" + id +
", produceName='" + produceName + '\'' +
'}';
}
}
class breadContainer{
private bread[] breads=new bread[6];
private int index=-1;
public synchronized void add(bread b){
while(index>=5){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
index++;
breads[index]=b;
System.out.println(Thread.currentThread().getName() + "生产了" + b.getId());
this.notifyAll();
}
public synchronized void take(){
while (index<0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
bread b=breads[index];
breads[index]=null;
System.out.println(Thread.currentThread().getName() + "消费了" + b.getId());
index--;
this.notifyAll();
}
}
class Producer implements Runnable{
private breadContainer breadContainer;
public Producer(pm.breadContainer breadContainer) {
this.breadContainer = breadContainer;
}
@Override
public void run() {
for (int i = 0; i < 30; i++) {
breadContainer.add(new bread(i,Thread.currentThread().getName()));
}
}
}
class Consumer implements Runnable{
private breadContainer breadContainer;
public Consumer(pm.breadContainer breadContainer) {
this.breadContainer = breadContainer;
}
@Override
public void run() {
for (int i = 0; i < 30; i++) {
breadContainer.take();
}
}
}