多线程学习笔记(四)之线程间通信---等待唤醒机制
线程间通信的一个demo
线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务。线程间通信,即多个线程在处理同一资源,但是任务却不同。例如我们现在有两个任务,分别是Input信息和Output信息,用之前(一)到(三)提到的synchronized+锁的方法解决的代码如下(加锁(同步)的问题需要考虑的本质因素,一个锁下是否有多个线程,即多个线程需要持有同一个锁对象),当执行完同步代码块时会释放持有的锁:
class Resource{
String name;
String sex;
}
//输入
class Input implements Runnable{
Resource r;
Input(Resource r){
this.r = r;
}
public void run(){
int x = 0;
while (true){
synchronized (r){
if (x==0){
r.name = "mike";
r.sex = "male";
}else {
r.name = "丽丽";
r.sex = "女";
}
}
x = (x+1)%2;
}
}
}
//输出
class Output implements Runnable{
Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while (true){
synchronized (r){
System.out.println(r.name+"......"+r.sex);
}
}
}
}
public class ThreadCommunication {
public static void main(String[] args){
//创建资源
Resource r = new Resource();
//创建任务
Input in = new Input(r);
Output out = new Output(r);
//创建线程,即执行路径
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
//开启线程
t1.start();
t2.start();
}
}
我们可以通过结果看到线程的安全问题已经得到解决,但是由于cpu的切换是随机的,因此这种方法不能满足“一个Input,一个Output”的需求。
等待唤醒机制
线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务。Object类中相关的方法有两个notify方法和三个wait方法。这些方法都是final的,即它们都是不能被重写的,不能通过子类覆写去改变它们的行为。
这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法,必须要明确到底操作的是哪个锁上的线程,
例如持有A锁的线程wait(),持有B锁的线程中进行notify()是没有用的
为什么操作线程的方法wait,notify,notifyAll定义在了Object类中:
因为这些方法是监视器的方法,监视器其实就可以看作是锁,监控线程的运行状态,锁可以是任意的对象,
任意的对象都可以调用的方法一定是在Object类中
wait()方法
让线程处于冻结状态,释放cpu的执行权和执行资格,被wait的线程会被存储到线程池中。线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。
wait()与sleep()方法的一个对比:
当线程调用了wait()方法时,它会释放掉对象的锁。另一个会导致线程暂停的方法:Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。
notify()方法
notify()方法会唤醒一个等待当前对象的锁的线程。
如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。(线程等待一个对象的锁是由于调用了wait方法中的一个)。
被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁。
被唤醒的线程将和其他线程以通常的方式进行竞争,来获得对象的锁。也就是说,被唤醒的线程并没有什么优先权,也没有什么劣势,对象的下一个线程还是需要通过一般性的竞争。
notify()方法应该是被拥有对象的锁的线程所调用。换句话说,和wait()方法一样,notify方法调用必须放在synchronized方法或synchronized块中。
一个线程变为一个对象的锁的拥有者是通过下列三种方法:
1.执行这个对象的synchronized实例方法。
2.执行这个对象的synchronized语句块。这个语句块锁的是这个对象。
3.对于Class类的对象,执行那个类的synchronized、static方法。
notifyAll()方法
唤醒线程池中的所有线程,使其处于运行状态或者临时阻塞状态,即使线程具备了执行资格
实例
class Resource{
String name;
String sex;
boolean flag = false;
}
//输入
class Input implements Runnable{
Resource r;
Input(Resource r){
this.r = r;
}
public void run(){
int x = 0;
while (true){
synchronized (r){
if (r.flag){
try{
r.wait();//r锁调用wait
}catch (InterruptedException e) {
e.printStackTrace();
}
}
if (x==0){
r.name = "mike";
r.sex = "male";
}else {
r.name = "丽丽";
r.sex = "女";
}
r.flag = true;
r.notify();//若此时没有等待线程,进行空唤醒也是可以的
}
x = (x+1)%2;
}
}
}
//输出
class Output implements Runnable{
Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while (true){
synchronized (r){
if (!r.flag){
try{
r.wait();//r锁调用wait
}catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(r.name+"......"+r.sex);
r.flag = false;
r.notify();//唤醒r线程池中的线程
}
}
}
}
public class ThreadCommunication {
public static void main(String[] args){
//创建资源
Resource r = new Resource();
//创建任务
Input in = new Input(r);
Output out = new Output(r);
//创建线程,即执行路径
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
//开启线程
t1.start();
t2.start();
}
}
实例优化
//修改方向:在资源中加入同步的访问和输出方法
class Resource{
private String name;
private String sex;
boolean flag = false;
//同步函数的锁是this
public synchronized void set(String name,String sex){
if (flag){
try{
this.wait();//r锁调用wait
}catch (InterruptedException e) {
e.printStackTrace();
}
}
this.name = name;
this.sex = sex;
flag = true;
this.notify();
}
public synchronized void out(){
if (!flag){
try{
this.wait();//r锁调用wait
}catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(name+"......"+sex);
flag = false;
this.notify();//唤醒r线程池中的线程
}
}
//输入
class Input implements Runnable{
Resource r;
Input(Resource r){
this.r = r;
}
public void run(){
int x = 0;
while (true){
if (x==0){
r.set("mike","male");
}else {
r.set("丽丽","女");
}
x = (x+1)%2;
}
}
}
//输出
class Output implements Runnable{
Resource r;
Output(Resource r){
this.r = r;
}
public void run(){
while (true){
r.out();
}
}
}
public class ThreadCommunication {
public static void main(String[] args){
//创建资源
Resource r = new Resource();
//创建任务
Input in = new Input(r);
Output out = new Output(r);
//创建线程,即执行路径
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
//开启线程
t1.start();
t2.start();
}
}
还没有评论,来说两句吧...