(1)生产者和消费者问题(1)--对象的创建
package PC; public class GetThread implements Runnable{ private Student s; public GetThread(Student s){ this.s = s; } @Override public void run() { // TODO Auto-generated method stub System.out.println("名字"+s.name+"年龄"+s.age); } } package PC; public class SetThread implements Runnable{ private Student s; public SetThread(Student s){ this.s = s; } @Override public void run() { // TODO Auto-generated method stub s.age =11; s.name="111"; } } package PC; public class Student { String name; int age; } package PC; public class StudentDemo { public static void main(String[] args) { Student s = new Student(); SetThread st = new SetThread(s); GetThread gt = new GetThread(s); Thread t1 = new Thread(st); Thread t2 = new Thread(gt); t1.start(); t2.start(); } }
运行结果 null 0
分析:线程运行时,每一次设置和获取值得时候,都会创建一个新的对象,解决方法:在外界把这个对象创建出来,通过构造方法传递对象。
(2)生产者和消费者问题(2)
package PC1; /** * * @author Administrator * * 为了数据的效果好。加入了循环和判断,给出不同的值,产生新的问题 * 问题: * A:同一个数据出现多次, * CPU的一点点时间片,就足够执行多次 * B:姓名和年龄不匹配 * * 线程安全问题: * A: 是否是多线程环境 * B:是否有共享数据 * C:是否有多条语句操作共享数据 * * 解决方案:加锁 */ public class StudentDemo { public static void main(String[] args) { Student s = new Student(); SetThread st = new SetThread(s); GetThread gt = new GetThread(s); Thread t1 = new Thread(st); Thread t2 = new Thread(gt); t1.start(); t2.start(); } } package PC1; public class Student { String name; int age; } package PC1; public class SetThread implements Runnable{ private Student s; private int x = 0; public SetThread(Student s){ this.s = s; } @Override public void run() { // TODO Auto-generated method stub while(true){ if(x % 2 == 0 ){ s.age =11; // 做到这里,被别人抢到了执行权,线程安全问题 s.name="111"; }else{ s.age =22; s.name="222";// 做到这里,被别人抢到了执行权线程安全问题 } x++; } } } package PC1; public class GetThread implements Runnable{ private Student s; public GetThread(Student s){ this.s = s; } @Override public void run() { while(true){ // TODO Auto-generated method stub System.out.println("名字"+s.name+"年龄"+s.age); } } }
运行结果
1111 11
1111 11
2222 22
改进方案
package PC1; /** * * @author Administrator * * 为了数据的效果好。加入了循环和判断,给出不同的值,产生新的问题 * 问题: * A:同一个数据出现多次, * CPU的一点点时间片,就足够执行多次 * B:姓名和年龄不匹配 * * 线程安全问题: * A: 是否是多线程环境 * B:是否有共享数据 * C:是否有多条语句操作共享数据 * * 解决方案:加锁 * 注意:(1)不同种类的对象都要加锁 * (2)不同种类的线程加的锁必须是同一把锁 */ public class StudentDemo { public static void main(String[] args) { Student s = new Student(); SetThread st = new SetThread(s); GetThread gt = new GetThread(s); Thread t1 = new Thread(st); Thread t2 = new Thread(gt); t1.start(); t2.start(); } } package PC1; public class SetThread implements Runnable{ private Student s; private int x = 0; public SetThread(Student s){ this.s = s; } @Override public void run() { // TODO Auto-generated method stub while(true){ synchronized (s) { if(x % 2 == 0 ){ s.age =11; // 做到这里,被别人抢到了执行权,线程安全问题 s.name="111"; }else{ s.age =22; s.name="222";// 做到这里,被别人抢到了执行权线程安全问题 } x++; } } } } package PC1; public class GetThread implements Runnable{ private Student s; public GetThread(Student s){ this.s = s; } @Override public void run() { while(true){ // TODO Auto-generated method stub synchronized (s) { System.out.println("名字"+s.name+"年龄"+s.age); } } } }
现在解决了 线程安全问题,但是还是会出现多条数据,主要是因为,cup短时间内会执行多次
(3)消费者生产者问题(3)
上面将线程安全的问题解决了,下面还存在一些问题。
(1).如果消费者先抢到cup执行权,就回去消费数据,但是现在使用的是默认值。此时的数据没有意义,
应该等着数据有意义,再去消费。
(2)如果生产者先抢到cpu执行权,就回去产生数据,但是呢,它产生完数据后,还继续拥有执行权,
它有继续产生数据。这还是有问题的,你应该等到消费者吧数据消费掉,然后在产生。
解决为题的思路是:
A :生产者
先看是否有数据,有数据等待,没有数据生产,生产之后,通知消费者消费。
B:消费者
先看是否有数据,有数据消费,没有数据等待。通知生产者,你该生产了。
java提供了一种机制:等待唤醒机制。
(4)消费者生产者之等待唤醒机制
package PC2; /** * * @author Administrator * * 为了数据的效果好。加入了循环和判断,给出不同的值,产生新的问题 * 问题: * A:同一个数据出现多次, * CPU的一点点时间片,就足够执行多次 * B:姓名和年龄不匹配 * * 线程安全问题: * A: 是否是多线程环境 * B:是否有共享数据 * C:是否有多条语句操作共享数据 * * 解决方案:加锁 * 注意:(1)不同种类的对象都要加锁 * (2)不同种类的线程加的锁必须是同一把锁 * * 问题3 虽然数据安全了 ,但是数据出现混乱,想要实现数据依次出现,通过java等待唤醒机制出现。 * 等待唤醒: * wait(); * notify(); * notifyAll(); * 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象,所以,这些方法必须定义在Object中。 */ public class StudentDemo { public static void main(String[] args) { Student s = new Student(); SetThread st = new SetThread(s); GetThread gt = new GetThread(s); Thread t1 = new Thread(st); Thread t2 = new Thread(gt); t1.start(); t2.start(); } } package PC2; public class Student { String name; int age; boolean flag; // 默认没有数据,如果输true有数据 } package PC2; public class SetThread implements Runnable{ private Student s; private int x = 0; public SetThread(Student s){ this.s = s; } @Override public void run() { // TODO Auto-generated method stub while(true){ synchronized (s) { // 判断是否有数据 if(s.flag){ try { s.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } }else{ if(x % 2 == 0 ){ s.age =11; // 做到这里,被别人抢到了执行权,线程安全问题 s.name="111"; }else{ s.age =22; s.name="222";// 做到这里,被别人抢到了执行权线程安全问题 } x++; } // 有数据 s.flag = true; // 唤醒 s.notify(); } } } } package PC2; public class GetThread implements Runnable{ private Student s; public GetThread(Student s){ this.s = s; } @Override public void run() { while(true){ // TODO Auto-generated method stub 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(); } } } }
运行结果
111 11
222 22
111 11
222 22
(5)生产者消费者代码优化(Student 对象私有)
package PC2; public class Student { private String name; private int age; boolean flag; // 默认没有数据,如果输true有数据 public synchronized void set(String name,int age) { // 如果有数据,等待 if(this.flag){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //设置数据 this.age = age; this.name = name; // 修改标记 this.flag = true; this.notify(); } public synchronized void get() { if(!this.flag){ try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(this.name+""+this.age); // 修改标记 this.flag = false; this.notify(); } } package PC2; public class SetThread implements Runnable{ private Student s; private int x = 0; public SetThread(Student s){ this.s = s; } @Override public void run() { // TODO Auto-generated method stub if(x % 2 == 0 ){ s.set("11", 11); }else{ s.set("22", 22); } x++; } } package PC2; public class GetThread implements Runnable{ private Student s; public GetThread(Student s){ this.s = s; } @Override public void run() { while(true){ s.get(); } } } package PC2; /** * * @author Administrator * * 为了数据的效果好。加入了循环和判断,给出不同的值,产生新的问题 * 问题: * A:同一个数据出现多次, * CPU的一点点时间片,就足够执行多次 * B:姓名和年龄不匹配 * * 线程安全问题: * A: 是否是多线程环境 * B:是否有共享数据 * C:是否有多条语句操作共享数据 * * 解决方案:加锁 * 注意:(1)不同种类的对象都要加锁 * (2)不同种类的线程加的锁必须是同一把锁 * * 问题3 虽然数据安全了 ,但是数据出现混乱,想要实现数据依次出现,通过java等待唤醒机制出现。 * 等待唤醒: * wait(); * notify(); * notifyAll(); * 这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象,所以,这些方法必须定义在Object中。 * * 最终代码 :吧student对象的成员私有了,把设置和获取封装成功能,并且加了同步,直接调用方法即可。 */ public class StudentDemo { public static void main(String[] args) { Student s = new Student(); SetThread st = new SetThread(s); GetThread gt = new GetThread(s); Thread t1 = new Thread(st); Thread t2 = new Thread(gt); t1.start(); t2.start(); } }