关于线程等待、线程唤醒方法的引入
public final void wait() throws InterruptedException 线程等待
public final void notify() 线程唤醒
1 public class Student { 2 private String name; 3 private int age; 4 5 public Student(String name, int age) { 6 super(); 7 this.name = name; 8 this.age = age; 9 } 10 11 public Student() { 12 13 } 14 15 public int getAge() { 16 return age; 17 } 18 19 public void setAge(int age) { 20 this.age = age; 21 } 22 23 public String getName() { 24 return name; 25 } 26 27 public void setName(String name) { 28 this.name = name; 29 } 30 31 @Override 32 public String toString() { 33 return "Student [name=" + name + ", age=" + age + "]"; 34 } 35 36 } 37 38 /** 39 * 生产学生的类 40 * 41 * @author Administrator 42 * 43 */ 44 public class SetStudent implements Runnable { 45 private Student student; 46 47 // 可以通过构造方法确定操作的是同一个学生 48 public SetStudent(Student student) { 49 this.student = student; 50 } 51 52 // 重写的run方法,让他调用生产学生的方法 53 @Override 54 public void run() { 55 createStudent(); 56 } 57 58 // 生产学生”张三“ 59 public void createStudent() { 60 while (true) { 61 student.setName("张三"); 62 student.setAge(11); 63 } 64 } 65 66 } 67 68 69 /** 70 * 消费学生的类:输出学生 71 * 72 * @author Administrator 73 * 74 */ 75 public class GetStudent implements Runnable { 76 private Student student; 77 78 @Override 79 public void run() { 80 getStudent(); 81 82 } 83 84 public GetStudent(Student student) { 85 this.student = student; 86 } 87 88 // 输出学生 89 public void getStudent() { 90 while (true) { 91 System.out.println(student); 92 } 93 94 } 95 } 96 97 98 public class StudentTest { 99 public static void main(String[] args) { 100 // 主方法创建一个学生,并通过参数传递赋给生产者和消费者,引用变量,传递同一个学生的引用地址,保证操作的都是一个对象 101 Student student = new Student(); 102 SetStudent setStu = new SetStudent(student); 103 GetStudent getStu = new GetStudent(student); 104 // 创建线程 105 Thread setStudent = new Thread(setStu); 106 Thread getStudent = new Thread(getStu); 107 // 开启线程 108 setStudent.start(); 109 getStudent.start(); 110 } 111 } 112 //执行结果: 113 //Student [name=张三, age=11] 114 //Student [name=张三, age=11] 115 //Student [name=张三, age=11] 116 //Student [name=张三, age=11] 117 //Student [name=张三, age=11] 118 //Student [name=张三, age=11] 119 //Student [name=张三, age=11] 120 //Student [name=张三, age=11] 121 //Student [name=张三, age=11] 122 //Student [name=张三, age=11] 123 //Student [name=张三, age=11] 124 //Student [name=张三, age=11] 125 //Student [name=张三, age=11] 126 //Student [name=张三, age=11] 127 //Student [name=张三, age=11] 128 //Student [name=张三, age=11] 129 /** 130 * 这里可以看到生产者和消费者使用的对象都是同一个,但是,如果生产者生产的是两个人,张三和大黄蜂呢? 131 */
1 /** 2 * 生产学生的类 3 * 4 * @author Administrator 5 * 6 */ 7 public class SetStudent implements Runnable { 8 private Student student; 9 private int index; 10 11 // 可以通过构造方法确定操作的是同一个学生 12 public SetStudent(Student student) { 13 this.student = student; 14 } 15 16 // 重写的run方法,让他调用生产学生的方法 17 @Override 18 public void run() { 19 createStudent(); 20 } 21 22 // 生产学生”张三“ 23 public void createStudent() { 24 while (true) { 25 if (index % 2 == 0) { 26 student.setName("张三"); 27 student.setAge(11); 28 } else { 29 student.setName("大黄蜂"); 30 student.setAge(30); 31 } 32 index++; // 当为奇数时生产大黄蜂,当为偶数时生产张三 33 } 34 } 35 36 } 37 38 39 public class StudentTest { 40 public static void main(String[] args) { 41 // 主方法创建一个学生,并通过参数传递赋给生产者和消费者,引用变量,传递同一个学生的引用地址,保证操作的都是一个对象 42 Student student = new Student(); 43 SetStudent setStu = new SetStudent(student); 44 GetStudent getStu = new GetStudent(student); 45 // 创建线程 46 Thread setStudent = new Thread(setStu); 47 Thread getStudent = new Thread(getStu); 48 // 开启线程 49 setStudent.start(); 50 getStudent.start(); 51 } 52 } 53 //执行结果: 54 //Student [name=大黄蜂, age=11] 55 //Student [name=大黄蜂, age=30] 56 //Student [name=大黄蜂, age=11] 57 //Student [name=大黄蜂, age=11] 58 //Student [name=大黄蜂, age=11] 59 //Student [name=张三, age=11] 60 //Student [name=大黄蜂, age=11] 61 //Student [name=大黄蜂, age=11] 62 //Student [name=大黄蜂, age=30] 63 //Student [name=大黄蜂, age=30] 64 //Student [name=张三, age=30] 65 //Student [name=张三, age=11] 66 //Student [name=大黄蜂, age=11] 67 //Student [name=张三, age=11] 68 //Student [name=大黄蜂, age=30] 69 //Student [name=张三, age=11] 70 //Student [name=大黄蜂, age=11] 71 /** 72 * 这里虽然生产出来了不同的对象,但是年龄和名字好像不对号 73 * 解释:在同一个时间点,CPU只能执行一条指令,在生产者抢到CPU给名字赋值张三时, 74 * 消费者抢到了Cpu要输出,所以年龄并没赋值,用的是之前的年龄 75 * 解决办法:加一个同步代码块 76 */
在生产学生处和消费学生方法增加同步代码块
// 生产学生”张三“ public void createStudent() { while (true) { synchronized (student) { if (index % 2 == 0) { student.setName("张三"); student.setAge(11); } else { student.setName("大黄蜂"); student.setAge(30); } index++; // 当为奇数时生产大黄蜂,当为偶数时生产张三 } } } // 输出学生 public void getStudent() { while (true) { synchronized (student) { System.out.println(student); try { Thread.sleep(100); // 输出太快了,模拟网络,让输出看起来均衡 } catch (InterruptedException e) { e.printStackTrace(); } } } } public class StudentTest { public static void main(String[] args) { // 主方法创建一个学生,并通过参数传递赋给生产者和消费者,引用变量,传递同一个学生的引用地址,保证操作的都是一个对象 Student student = new Student(); SetStudent setStu = new SetStudent(student); GetStudent getStu = new GetStudent(student); // 创建线程 Thread setStudent = new Thread(setStu); Thread getStudent = new Thread(getStu); // 开启线程 setStudent.start(); getStudent.start(); } } //执行结果: //Student [name=null, age=0] //Student [name=null, age=0] //Student [name=null, age=0] //Student [name=张三, age=11] //Student [name=张三, age=11] //Student [name=张三, age=11] //Student [name=张三, age=11] //Student [name=张三, age=11] //Student [name=张三, age=11] //Student [name=张三, age=11] //Student [name=张三, age=11] //Student [name=张三, age=11] //Student [name=大黄蜂, age=30] //Student [name=大黄蜂, age=30] //Student [name=大黄蜂, age=30] //Student [name=大黄蜂, age=30] //Student [name=大黄蜂, age=30] //Student [name=大黄蜂, age=30] //Student [name=大黄蜂, age=30] //Student [name=大黄蜂, age=30] /** * 年龄对号,但是在生产者中添加了index,就是为了让奇数偶数一个一个分别输出,但是情况不对 * 解释:虽然加了同步代码块,但是两个线程CPU的抢占时间是随机的,消费者一直抢占CPU就一直输出,但是生产者还没有生产出来,消费者不该输出 * 解决办法:使用线程等待: * 生产者先生产,消费者再消费 生产者没有生产,消费者等待 生产者已经生产,消费者没有消费,生产者等待 */
完整
1 public class Student { 2 private String name; 3 private int age; 4 boolean flag = true; // 当为真时,生产者才生产 5 6 public Student(String name, int age) { 7 super(); 8 this.name = name; 9 this.age = age; 10 } 11 12 public Student() { 13 14 } 15 16 public int getAge() { 17 return age; 18 } 19 20 public void setAge(int age) { 21 this.age = age; 22 } 23 24 public String getName() { 25 return name; 26 } 27 28 public void setName(String name) { 29 this.name = name; 30 } 31 32 @Override 33 public String toString() { 34 return "Student [name=" + name + ", age=" + age + "]"; 35 } 36 37 } 38 39 40 /** 41 * 生产学生的类 42 * 43 * @author Administrator 44 * 45 */ 46 public class SetStudent implements Runnable { 47 private Student student; 48 private int index; 49 50 // 可以通过构造方法确定操作的是同一个学生 51 public SetStudent(Student student) { 52 this.student = student; 53 } 54 55 // 重写的run方法,让他调用生产学生的方法 56 @Override 57 public void run() { 58 createStudent(); 59 } 60 61 // 生产学生”张三“ 62 public void createStudent() { 63 while (true) { 64 synchronized (student) { 65 if (student.flag) { // 当flag为真时,已经生产,等待消费者消费,归还锁,当被唤醒就从这里开始 66 try { 67 student.wait(); 68 } catch (InterruptedException e) { 69 e.printStackTrace(); 70 } 71 } 72 if (index % 2 == 0) { 73 student.setName("张三"); 74 student.setAge(11); 75 } else { 76 student.setName("大黄蜂"); 77 student.setAge(30); 78 } 79 80 index++; // 当为奇数时生产大黄蜂,当为偶数时生产张三 81 student.flag = true; // 唤醒生产者,让他抢CPU 82 student.notify(); 83 // 84 } 85 } 86 } 87 88 } 89 90 91 /** 92 * 消费学生的类:输出学生 93 * 94 * @author Administrator 95 * 96 */ 97 public class GetStudent implements Runnable { 98 private Student student; 99 100 @Override 101 public void run() { 102 getStudent(); 103 104 } 105 106 public GetStudent(Student student) { 107 this.student = student; 108 } 109 110 // 输出学生 111 public void getStudent() { 112 while (true) { 113 synchronized (student) { 114 // 等于true时,生产者已经生产,此时消费者才可以消费,反之,当是false的时候,就应该等待生产者生产 115 if (!student.flag) { 116 try { 117 student.wait(); 118 } catch (InterruptedException e) { 119 e.printStackTrace(); 120 } 121 } 122 System.out.println(student); 123 student.flag = false; // 改变参数,并且唤醒消费者线程,让他抢CPU 124 student.notify(); 125 } 126 127 } 128 129 } 130 } 131 132 public class StudentTest { 133 public static void main(String[] args) { 134 // 主方法创建一个学生,并通过参数传递赋给生产者和消费者,引用变量,传递同一个学生的引用地址,保证操作的都是一个对象 135 Student student = new Student(); 136 SetStudent setStu = new SetStudent(student); 137 GetStudent getStu = new GetStudent(student); 138 // 创建线程 139 Thread setStudent = new Thread(setStu); 140 Thread getStudent = new Thread(getStu); 141 // 开启线程 142 setStudent.start(); 143 getStudent.start(); 144 } 145 } 146 //执行结果: 147 //Student [name=张三, age=11] 148 //Student [name=大黄蜂, age=30] 149 //Student [name=张三, age=11] 150 //Student [name=大黄蜂, age=30] 151 //Student [name=张三, age=11] 152 //Student [name=大黄蜂, age=30] 153 //Student [name=张三, age=11] 154 //Student [name=大黄蜂, age=30] 155 //Student [name=张三, age=11]