Java实现--线程间通信问题案例
2017-05-22 22:45
561 查看
案例:给学生设置和获取姓名和年龄。
案例分析:我们把学生对象作为一个资源进行操作,利用设置线程给学生对象设置姓名和年龄,利用获取线程获取姓名和年龄。
图解:
可以看出这是一个单生产单消费问题。
代码实现:
Student – 被设置的资源对应的类
SetThread – 设置线程
GetThread – 获取线程
StudnetDemo – 测试类
运行结果:
这个案例实现过程中遇到的问题:
(1)打印null–0:
原因:设置和获取不是同一个对象,在设置线程和获取线程中new了两次,所以创建了两个学生对象。
解决:当在不同的类中,需要使用同一个数据的时候,可以考虑将数据利用构造方法进行传参。
(2)出现数据错乱,线程安全问题:
原因:是多线程情况;存在共享数据;存在多条语句对共享数据进行操作。所以存在线程安全问题。
解决:同步代码块,加锁。设置线程和获取线程,加的锁必须是同一个。
(3)打印的结果不够和谐:
原因:上面那个代码开启测试类后,会开启两个线程,一个是设置线程,一个是获取线程,然而我们并没有决定让谁抢到CPU的执行权。
对于第三个问题,我们可以使用等待唤醒机制来改进,实现礼让效果。
等待唤醒机制用到的几个方法:
注意:这几个方法都只能通过锁对象调用,只有在同步的时候,这几个方法才有效果。
改进后的代码:
Student – 被设置的资源对应的类
SetThread – 设置线程
GetThread – 获取线程
StudnetDemo – 测试类
运行结果:
对上面代码继续优化:
(1)私有化Student类的成员变量
(2)在类的内部提供设置和获取的同步方法
代码实现:
Student – 被设置的资源对应的类
SetThread – 设置线程
GetThread – 获取线程
StudnetDemo – 测试类
运行结果:
9793
案例分析:我们把学生对象作为一个资源进行操作,利用设置线程给学生对象设置姓名和年龄,利用获取线程获取姓名和年龄。
图解:
可以看出这是一个单生产单消费问题。
代码实现:
Student – 被设置的资源对应的类
package com.edu_01; public class Student { String name; int age; }
SetThread – 设置线程
package com.edu_01; public class SetThread extends Thread{ private Student s; private int x=0; public SetThread(Student s){ this.s=s; } @Override public void run() { while (true) { synchronized (s) { if (x%2==0) { s.name="张三"; s.age=22; } else { s.name="李四"; s.age=23; } x++; } } } }
GetThread – 获取线程
package com.edu_01; public class GetThread extends Thread{ private Student s; public GetThread(Student s){ 4000 this.s=s; } @Override public void run() { while (true) { synchronized (s) { System.out.println(s.name+":"+s.age); } } } }
StudnetDemo – 测试类
package com.edu_01; public class StudentDemo { public static void main(String[] args) { //创建学生对象 Student s = new Student(); //创建设置线程和获取线程并开启 SetThread st = new SetThread(s); GetThread gt = new GetThread(s); st.start(); gt.start(); } }
运行结果:
这个案例实现过程中遇到的问题:
(1)打印null–0:
原因:设置和获取不是同一个对象,在设置线程和获取线程中new了两次,所以创建了两个学生对象。
解决:当在不同的类中,需要使用同一个数据的时候,可以考虑将数据利用构造方法进行传参。
(2)出现数据错乱,线程安全问题:
原因:是多线程情况;存在共享数据;存在多条语句对共享数据进行操作。所以存在线程安全问题。
解决:同步代码块,加锁。设置线程和获取线程,加的锁必须是同一个。
(3)打印的结果不够和谐:
原因:上面那个代码开启测试类后,会开启两个线程,一个是设置线程,一个是获取线程,然而我们并没有决定让谁抢到CPU的执行权。
对于第三个问题,我们可以使用等待唤醒机制来改进,实现礼让效果。
等待唤醒机制用到的几个方法:
wait()//让该线程处于等待状态 notify()//唤醒等待的线程,如果没有线程在等待,则不起作用 notifyAll()//唤醒所有等待的线程
注意:这几个方法都只能通过锁对象调用,只有在同步的时候,这几个方法才有效果。
改进后的代码:
Student – 被设置的资源对应的类
package com.edu_02; public class Student { String name; int age; boolean flag;//用来判断对象中是否有数据 }
SetThread – 设置线程
package com.edu_02; public class SetThread implements Runnable{ private Student s; private int x=0; public SetThread(Student s){ this.s=s; } @Override public void run() { while (true) { synchronized (s) { //判断此时对象中有没有数据 if (s.flag) { try { s.wait();//设置线程等待,同时释放锁s } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if (x%2==0) { s.name="张三"; s.age=22; } else { s.name="李四"; s.age=33; } x++; s.flag=true;//此时对象中有数据了 s.notify();//唤醒正在等待的线程,如果没有线程等待则没有任何效果 } } } }
GetThread – 获取线程
package com.edu_02; public class GetThread implements Runnable{ private Student s; public GetThread(Student s){ this.s=s; } @Override public void run() { while (true) { synchronized (s) { //判断对象中是否有数据 if (!s.flag) { try { s.wait();//获取线程等待,释放锁s } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(s.name+":"+s.age); s.flag=false;//当获取线程从对象中获取数据后,我们默认对象中没有数据了,此时我们应该让设置线程继续给学生对象设置学生信息 s.notify(); } } } }
StudnetDemo – 测试类
package com.edu_02; 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(); } }
运行结果:
对上面代码继续优化:
(1)私有化Student类的成员变量
(2)在类的内部提供设置和获取的同步方法
代码实现:
Student – 被设置的资源对应的类
package com.edu_03; public class Student { //私有化成员变量 private String name; private int age; private boolean flag;//用来标记学生对象中是否有信息 //提供公共方法设置信息 public synchronized void setInfo(String name,int age){ if (this.flag) { try { this.wait();//等待 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //如果学生对象中没有信息,则设置信息 this.name=name; this.age=age; //更改标记,唤醒线程 this.flag=true; this.notify(); } //提供公共方法获取信息 public synchronized void getInfo(){ 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(); } }
SetThread – 设置线程
package com.edu_03; public class SetThread implements Runnable{ private Student s; private int x=0; public SetThread(Student s){ this.s=s; } @Override public void run() { while (true) { if (x%2==0) { s.setInfo("张三",22); } else { s.setInfo("李四",33); } x++; } } }
GetThread – 获取线程
package com.edu_01; public class GetThread implements Runnable{ private Student s; public GetThread(Student s){ this.s=s; } @Override public void run() { while (true) { s.getInfo(); } } }
StudnetDemo – 测试类
package com.edu_03; 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(); } }
运行结果:
9793
相关文章推荐
- Thinking in Java---线程通信+三种方式实现生产者消费者问题
- 【Java】线程wait() notify()通信 实现生产者 消费者问题
- Java线程的方式实现TCP通信的小案例
- Java线程间通信-回调的实现方式
- Java通过wait()和notifyAll()方法实现线程间的通信
- java中使用ReentrantLock锁中的Condition实现三个线程之间通信,交替输出信息
- java之管道流实现线程间的通信(图文版)
- Java里如何实现线程间通信?
- JAVA学习笔记(1)_____模拟线程通信之生产者消费者问题
- Java 线程实现生产者和消费者问题
- 线程通信,生产者消费者问题(Java)
- Java 里如何实现线程间通信
- Android(java)学习笔记70:同步中的死锁问题以及线程通信问题
- 【Java并发编程】之十二:线程间通信中notifyAll造成的早期通知问题(含代码)
- 【Java并发编程】之十二:线程间通信中notifyAll造成的早期通知问题(含代码)
- Java里如何实现线程间通信?
- Java线程间通信-回调的实现方式
- 黑马程序员——JAVA基础---线程之间的通信实现交替输出打印
- Java 里如何实现线程间通信
- 【Java并发编程】之十:使用wait/notify/notifyAll实现线程间通信的几点重要说明