同步机制的弊端:
1、执行效率低(同步的线程进入后,别的线程无法进入)
2、容易发生死锁现象,即两个线程互相等待对方释放同步监视器时发生思死锁
加入生产者(生产数据)消费者(消费数据)。
资源对象Star类:
public class Star {
private String name;
private int age;
public Star() {
super();
// TODO Auto-generated constructor stub
}
public Star(String name, int age) {
if(name!=null){
this.name = name;
}else{
System.out.println("请输入姓名!");
System.exit(0);
}
if(age>0){
this.age = age;
}else{
System.out.println("输入正确年龄!");
System.exit(0);
}
}
public String getName() {
return name;
}
public void setName(String name) {
}
public int getAge() {
return age;
}
public void setAge(int age) {
}
}
生产者类:
public class SetStar implements Runnable {
private Star s;
public SetStar(Star s){
this.s = s;
}
@Override
public void run() {
synchronized (s) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("set线程名:" + Thread.currentThread().getName() + "\t姓名:"
+ s.getName() + "\t年龄:" + s.getAge());
}
}
}
消费者类:
public class GetStar implements Runnable {
private Star s;
public GetStar(Star s) {
this.s = s;
}
@Override
public void run() {
StringBuffer sb = new StringBuffer();
synchronized (s) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
sb.append("get线程名:").append(Thread.currentThread().getName())
.append("\t姓名:").append(s.getName()).append("\t年龄:")
.append(s.getAge());
}
System.out.println(sb);
}
}
测试类:
package Day21_produce_consume;
import java.util.Scanner;
/**
*屏幕录入人物信息,然后多线程实现设置人物信息,获取人物信息
* @author Aoman_Hao
*/
public class StarTest {
public static void main(String[] args) {
//屏幕录入
Scanner sc_name = new Scanner(System.in);
System.out.println("请输入人物姓名");
String name = sc_name.next();
Scanner sc_age = new Scanner(System.in);
System.out.println("年龄格式有误,请输入人物年龄");
int age = sc_age.nextInt();
//创建资源对象
Star s1 = new Star(name,age);
SetStar ss1 = new SetStar(s1);
GetStar gs1 = new GetStar(s1);
//创建线程对象
Thread sT1 = new Thread(ss1);
Thread gT1 = new Thread(gs1);
//启动线程对象
sT1.start();
gT1.start();
}
}
输出:
请输入人物姓名
刘诗诗
请输入人物年龄
22
set线程名:Thread-0 姓名:刘诗诗 年龄:22
get线程名:Thread-1 姓名:刘诗诗 年龄:22
以上为单次屏幕录入数据,若多线程加入循环操作
问题1:会出现年纪姓名不符合的现象,需要用synchronized同步锁对象操作。
问题2:数据打印多次,需要用唤醒机制来解决。
我们先加入同步操作
资源对象类:
public class Star {
private String name;
private int age;
public Star() {
super();
// TODO Auto-generated constructor stub
}
public Star(String name, int age) {
if(name!=null){
this.name = name;
}else{
System.out.println("姓名格式有误,请输入姓名!");
System.exit(0);
}
if(age>0){
this.age = age;
}else{
System.out.println("年龄格式有误,输入正确年龄!");
System.exit(0);
}
}
public String getName() {
return name;
}
public void setName(String name) {
}
public int getAge() {
return age;
}
public void setAge(int age) {
}
}
生产者类:
public class SetStar implements Runnable {
private Star s;
public SetStar(Star s){
this.s = s;
}
@Override
public void run() {
//加入循环,多线程出现数据错位,应使用同步代码块
while(true){
synchronized (s) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("set线程名:" + Thread.currentThread().getName() + "\t姓名:"
+ s.getName() + "\t年龄:" + s.getAge());
}
}
}
}
消费者类:
public class GetStar implements Runnable {
private Star s;
public GetStar(Star s) {
this.s = s;
}
@Override
public void run() {
StringBuffer sb = new StringBuffer();
while(true){
synchronized (s) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
sb.append("get线程名:").append(Thread.currentThread().getName())
.append("\t姓名:").append(s.getName()).append("\t年龄:")
.append(s.getAge());
}
System.out.println(sb);
}
}
}
测试类:
package Day21_produce_consume_while;
import java.util.Scanner;
/**
*屏幕录入人物信息,然后多线程实现设置人物信息,获取人物信息
* @author Aoman_Hao
*/
public class StarTest {
public static void main(String[] args) {
//屏幕录入
Scanner sc_name = new Scanner(System.in);
System.out.println("请输入人物姓名");
String name = sc_name.next();
Scanner sc_age = new Scanner(System.in);
System.out.println("请输入人物年龄");
int age = sc_age.nextInt();
//创建资源对象
Star s1 = new Star(name,age);
SetStar ss1 = new SetStar(s1);
GetStar gs1 = new GetStar(s1);
//创建线程对象
Thread sT1 = new Thread(ss1);
Thread gT1 = new Thread(gs1);
//启动线程对象
sT1.start();
gT1.start();
}
}
输出(节选,无限循环):
请输入人物姓名
刘诗诗
请输入人物年龄
22
set线程名:Thread-0 姓名:刘诗诗 年龄:22
set线程名:Thread-0 姓名:刘诗诗 年龄:22
set线程名:Thread-0 姓名:刘诗诗 年龄:22
set线程名:Thread-0 姓名:刘诗诗 年龄:22
get线程名:Thread-1 姓名:刘诗诗 年龄:22
set线程名:Thread-0 姓名:刘诗诗 年龄:22
加入循环,打印的东西很多,我们想要一个set,一个get顺序完成。我们再运用等待唤醒机制。
资源对象类:
public class Star {
private String name;
private int age;
private boolean flag= false;//false为没有值
public boolean getflag(){
return flag;
}
public boolean setflag(boolean flag1){
return this.flag=flag1;
}
public Star() {
super();
}
public Star(String name, int age) {
if(name!=null){
this.name = name;
}else{
System.out.println("姓名格式有误,请输入姓名!");
System.exit(0);
}
if(age>0){
this.age = age;
}else{
System.out.println("年龄格式有误,输入正确年龄!");
System.exit(0);
}
}
public String getName() {
return name;
}
public void setName(String name) {
}
public int getAge() {
return age;
}
public void setAge(int age) {
}
}
生产者类:
public class SetStar implements Runnable {
private Star s;
public SetStar(Star s){
this.s = s;
}
@Override
public void run() {
//加入循环,多线程出现数据错位,应使用同步代码块
while(true){
synchronized (s) {
if(s.getflag()==true){
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("set线程名:" + Thread.currentThread().getName() + "\t姓名:"
+ s.getName() + "\t年龄:" + s.getAge());
//如果生产者有数据,唤醒消费者线程拿数据
s.setflag(true);
s.notify();
}
}
}
}
消费者类:
public class GetStar implements Runnable {
private Star s;
public GetStar(Star s) {
this.s = s;
}
@Override
public void run() {
while (true) {
synchronized (s) {
if (s.getflag()==false) {
try {
s.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
StringBuffer sb = new StringBuffer();
sb.append("get线程名:")
.append(Thread.currentThread().getName())
.append("\t姓名:").append(s.getName())
.append("\t年龄:").append(s.getAge());
System.out.println(sb);
//消费者线程没有数据,唤醒生产者线程生产数据
s.setflag(false);
s.notify();
}
}
}
}
测试类:
package Day21_produce_consume_flag;
import java.util.Scanner;
/**
*屏幕录入人物信息,然后多线程实现设置人物信息,获取人物信息
* @author Aoman_Hao
*/
public class StarTest {
public static void main(String[] args) {
//屏幕录入
Scanner sc_name = new Scanner(System.in);
System.out.println("请输入人物姓名");
String name = sc_name.next();
Scanner sc_age = new Scanner(System.in);
System.out.println("请输入人物年龄");
int age = sc_age.nextInt();
//创建资源对象
Star s1 = new Star(name,age);
SetStar ss1 = new SetStar(s1);
GetStar gs1 = new GetStar(s1);
//创建线程对象
Thread sT1 = new Thread(ss1);
Thread gT1 = new Thread(gs1);
//启动线程对象
sT1.start();
gT1.start();
}
}
输出(节选):
请输入人物姓名
刘诗诗
请输入人物年龄
22
set线程名:Thread-0 姓名:刘诗诗 年龄:22
get线程名:Thread-1 姓名:刘诗诗 年龄:22
set线程名:Thread-0 姓名:刘诗诗 年龄:22
get线程名:Thread-1 姓名:刘诗诗 年龄:22
set线程名:Thread-0 姓名:刘诗诗 年龄:22
get线程名:Thread-1 姓名:刘诗诗 年龄:22
set线程名:Thread-0 姓名:刘诗诗 年龄:22
get线程名:Thread-1 姓名:刘诗诗 年龄:22
set线程名:Thread-0 姓名:刘诗诗 年龄:22
get线程名:Thread-1 姓名:刘诗诗 年龄:22
效果:
生产者线程,处于等待的话,则证明生产了数据,且没有被消费。然后通知消费者线程去消费数据;
消费者线程输出数据,没有生产数据后,等待,唤醒生产者线程生产数据