您的位置:首页 > 编程语言 > Java开发

Java实现--线程间通信问题案例

2017-05-22 22:45 561 查看
案例:给学生设置和获取姓名和年龄。

案例分析:我们把学生对象作为一个资源进行操作,利用设置线程给学生对象设置姓名和年龄,利用获取线程获取姓名和年龄。

图解



可以看出这是一个单生产单消费问题。

代码实现

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: