要求逐一输出(张三--18,李四--20)
1.生产程序
package 生产消费2;
//生产数据
public class set implements Runnable{
private student s;
public set(student s) {
this.s=s;
}
int x=0;
public void run() {
while(true) {
synchronized (s) {//改进后加入锁
if(x%2==0) {
s.name="张三";
s.age=18;
}else {
s.name="李四";
s.age=20;
}
x++;
}
}
}
}
2.消费程序
package 生产消费2;
//消费数据
public class get implements Runnable{
private student s;
public get(student s) {
this.s=s;
}
public void run() {
while(true) {
synchronized (s) { //改进后加入的锁
System.out.println(s.name+"---"+s.age);
}
}
}
}
3.main程序
package 生产消费2;
//循环生产循环消费
//运行结果:李四---18
// 张三---20
//运行出现张三和李四的是随机的,不是想象中的逐一出现,且有错误数据
//所以要加入synchronized锁,在此运行
//运行后不再有错误数据,但是依然没有解决重复逐一打印
//原因:加锁之后起到的效果是在生产线程时不会出现数据错乱的效果,
//因为当一个线程进去后,其他线程无法进入,只有等前一个借宿才能进入,
//所以就解决的错误数据的问题
//消费中加入锁无意义,因为只有单一输出,不会有错误
public class rundemo {
public static void main(String[] args) {
//创建学生类对象
student s=new student();
//创建线程类对象
set se=new set(s);
get ge=new get(s);
//创建线程对象
Thread t1=new Thread(se);
Thread t2=new Thread(ge);
//启动线程
t1.start();
t2.start();
}
}
二.改进后的唤醒机制
1.生产程序
package 生产消费3;
//生产数据
public class set implements Runnable{
private student s;
private int x=0;
public set(student s) {
this.s=s;
}
public void run() {
while(true) {
//生产者加锁
synchronized (s) {
//判断是否有数据,没有数据则flag为flase
if(s.flag) {
//有数据,则s.wait等待,且抛异常
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产数据
if(x%2==0) {
s.name="张三";
s.age=18;
}else {
s.name="李四";
s.age=20;
}
x++;
//数据已经生产完,则s.flag是ture,也就是有数据
s.flag=true;
//唤醒get(消费)
s.notify();
}
}
}
}
2.消费程序
package 生产消费3;
//消费数据
public class get implements Runnable{
private student s;
public get(student s) {
this.s=s;
}
public void run() {
while(true) {
synchronized (s) {
if(!s.flag) {
try {
s.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(s.name+"---"+s.age);
s.flag=false;
s.notify();
}
}
}
}
3.main程序
package 生产消费3;
//为了解决逐一打印问题,加入线程等待唤醒机制
//public final void notify()
//唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。
//选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
/**
* 唤醒流程:
* 两个线程一起执行:
* set执行,判断是否有数据,有数据则s.wait()等待,也就是不产出数据
* 没数据则则添加数据,添加完后执行s.flag=ture
* 将flag更改为错误,也就是有数据,再唤醒等待中的线程(也就是正在执行s.wait()的线程)
* s.notify()唤醒get数据
* get的判断等待的代码就接收到的是ture
* 就会输出消费数据
* get执行.判断是否有数据,有数据则输出数据,然后执行s.flag=flase
* 同是唤醒set数据,来产生数据
* 没数据则等待
* 中间的核心 1.有一个可以判断是否线程等待的boolean类型
* 2.唤醒机制,可以互相唤醒的功能
* 这样才实现了逐一输出的结果
*/
public class rundemo {
public static void main(String[] args) {
//创建学生类对象
student s=new student();
//创建线程类对象
set se=new set(s);
get ge=new get(s);
//创建线程对象
Thread t1=new Thread(se);
Thread t2=new Thread(ge);
//启动线程
t1.start();
t2.start();
}
}
三.死锁案例
package 死锁现象;
public class demo {
public static void main(String[] args) {
lock l1 = new lock(true);
lock l2 = new lock(false);
l1.start();
l2.start();
}
}
//输出可能的结果
//else objB
//else objA
//if ObjA
//if objB
//if ObjA
//if objB
//else objB
//else objA
//if obgA
//else objB
//else objB
//if obgA
package 死锁现象;
//出现锁死的原因:
/**run中有两个锁,锁对象分别是objA和objB
* 在进程l1和l2进来的的时候,
* 假设l1先进来(true),那么objA先执行,在执行obj2之前,
* l2(flase)进程进来执行了objB
* 这时objA和objB都被执行,但if或者else中的语句还没有执行完毕
* 继续向下执行,这时就出现了死锁
* 因为if中要执行的objB的锁在else中被占中,所以等待else执行完后执行后释放objB,然后if执行
* else中要执行的objA的锁在if中被占中,所以等待if执行完后执行后释放objB,然后else执行
* 就出现了互相等待的情况,都执行一般等待对方释放,这就是死锁
* 但是也有特殊情况:比如if或者else语句都执行完毕后再执行其他语句
* 这样就释放了objA和objB的锁,就不会出现死锁情况
*
*
*
*/
public class lock extends Thread{
private boolean flag;
public static final Object objA = new Object() ;
public static final Object objB = new Object() ;
public lock( boolean flag) {
this.flag=flag;
}
@Override
public void run() {
if(flag) {
synchronized (objA) {
System.out.println("if obgA");
synchronized (objB) {
System.out.println("if bogB");
}
}
}else {
synchronized (objB) {
System.out.println("else objB");
synchronized (objA) {
System.out.println("else objA");
}
}
}
}
}