一、“监视线程”通讯模型
概念:同时多个独立线程在运行,运行状态由第三方监控线程全程监控,这种模型为监视线程模型
如下所示:
二、线程同步问题
多线程程序可能存在的一个问题就是:可能会出现多个线程同时操作某一个对象的数据,结果就会出现线程对象对自己操作的数据不同步的情况。
如图:
例子:
package com.test.thread;
/**
* 账户类
*
* @author lhz
*
*/
public class Account {
private int count;
public Account(int count) {
this.count = count;
}
/**
* 取现的方法
*/
public int getCount(int cash) {
// synchronized (this) {
if (count < cash) {
System.out.println("你的余额不足,余额为" + count + "取现失败");
return -1;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count = count - cash;
// }
return count;
}
}
package com.test.thread;
/**
* 客户线程类
*
* @author lhz
*
*/
public class Costomer extends Thread {
private Account account;
private int cash;
private String type;
public Costomer(Account account, int cash, String type) {
this.cash = cash;
this.account = account;
this.type = type;
}
/**
* 取现
*/
public void run() {
int result = account.getCount(cash);
if (result < 0) {
System.out.println(type + "取现失败,您余额不足");
} else {
System.out.println(type + "取现成功,您取了" + cash + ",余额还有" + result);
}
}
}
package com.test.thread;
public class ThreadTest {
public static void main(String[] args) {
Account account = new Account(5000);
Costomer c1 = new Costomer(account, 4000, "atm");
Costomer c2 = new Costomer(account, 3000, "柜台");
c1.start();
c2.start();
}
}
会出现这里两种情况
第一张图:这个是因为,程序先执行c2.start(),调用getCount(int cash)方法,执行到休眠;c1.start()执行,执行到休眠;c2又执行,执行
count = count - cash;
此时count=2000;但是c1的count还是5000,然后c1执行
count = count - cash;
此时count=1000,最终 count=1000.
第二张图:这个是因为,程序先执行c1.start(),调用getCount(int cash)方法,执行到休眠;c2.start()执行,执行到休眠;c1又执行,执行
count = count - cash;
此时count=1000;但是c1的count还是5000,然后c1执行count = count - cash;
此时count=2000,最终 count=2000.
不管是那种情况,对于银行来说都是亏本的,如何解决这个问题呢?
三、synchronized:同步锁关键字,锁定当前对象或者方法,一家独用
简介:
1、synchronized代码块中的语句只能有一个线程在执行
2、任意一个对象都有一个标志位,有1和0两种状态
3、当程序执行到synchronized代码块的时候线程会检查对象的标志位是1还是0
4、如果是1则执行程序,同是将对象的标志位设置为0,其他线程执行到synchronized代码块时一看对象标志位为0 则线程会阻塞,一直等到对象的标志位为1再执行下面的程序
实例(还是上面的例子):锁定同一个对象
synchronized (this) {
if (count < cash) {
System.out.println("你的余额不足,余额为" + count + "取现失败");
return -1;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count = count - cash;
}
四、 wait/notify机制
在java中,每个对象都有从Object父类继承而来的两个关于线程间通讯的方法wait()和notify(),如其方法名所示,一个是等待,一个是通知,当在一个对象上调用wait()方法时,当前线程就会进行wait状态,直到收到另一个对象的notify()发出通知,才会执行下一步计算
在多线程通讯中,经常会用对象的这两个方法,一个典型的案例就是“生产/消费者模型”
五、生产/消费者模型
模型规则
生产和消费线程共同操作一个集合,生产线程放入对象,消费线程取出对象!仅当集合中没有对象时,生产线程会放入一个对象,如有集合中有一个对象时,消费线程要马上取出这个对象。
如图所示 :
实例:
package com.test.thread1;
/**
* 手机类
*
* @author lhz
*
*/
public class Phone {
public int type;
}
package com.test.thread1;
import java.util.ArrayList;
/**
* 消费者
*
* @author lhz
*
*/
public class Costomer extends Thread {
private Phone phone;
private ArrayList<Phone> list;
public Costomer(Phone phone, ArrayList<Phone> list) {
this.phone = phone;
this.list = list;
}
public void run() {
while (true) {
// synchronized (list) {
if (list.size() >= 1) {
list.remove(0);// 取出
// list.notify();
System.out.println("消费了" + phone.type);
phone.type++;
} else {
continue;
// try {
// list.wait();// 等待
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// }
}
}
}
package com.test.thread1;
import java.util.ArrayList;
/**
* 生产者
*
* @author lhz
*
*/
public class Product extends Thread {
private Phone phone;
private ArrayList<Phone> list;
public Product(Phone phone, ArrayList<Phone> list) {
this.phone = phone;
this.list = list;
}
public void run() {
while (true) {
// synchronized (list) {
if (list.size() <= 0) {
list.add(phone);// 放入
// list.notify();// 通知
System.out.println("生产出" + phone.type);
phone.type++;
} else {
continue;
// try {
// list.wait();// 等待
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// }
}
}
}
package com.test.thread1;
import java.util.ArrayList;
public class TestThread {
public static void main(String[] args) {
Phone phone = new Phone();
ArrayList<Phone> list = new ArrayList<Phone>();
Product p = new Product(phone, list);
Costomer c = new Costomer(phone, list);
p.start();
c.start();
}
}
cpu消耗非常大,效率低
使用了wait/notify,但是没有使用同步锁
while (true) {
// synchronized (list) {
if (list.size() >= 1) {
list.remove(0);// 取出
list.notify();
System.out.println("消费了" + phone.type);
phone.type++;
} else {
try {
list.wait();// 等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// }
}
while (true) {
// synchronized (list) {
if (list.size() <= 0) {
list.add(phone);// 放入
list.notify();// 通知
System.out.println("生产出" + phone.type);
phone.type++;
} else {
try {
list.wait();// 等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
// }
}
加了同步锁
while (true) {
synchronized (list) {
if (list.size() <= 0) {
list.add(phone);// 放入
list.notify();// 通知
System.out.println("生产出" + phone.type);
phone.type++;
} else {
try {
list.wait();// 等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
while (true) {
synchronized (list) {
if (list.size() >= 1) {
list.remove(0);// 取出
list.notify();
System.out.println("消费了" + phone.type);
phone.type++;
} else {
try {
list.wait();// 等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
cpu占内存少了,不会出现做无用功情况
注意:
1)wait和notify必须在同步锁之内使用
2)同步锁锁定对象和wait、notify对象必须同一个
3)当对象wait挂起状态时候是会释放同步锁的
六、线程生命周期