多线程学习笔记四--------------线程间通信问题

线程间通信问题:
多个线程在处理同一资源,但是任务却不同;
java中将资源共享的方法(思路):
1、方法或者变量静态化---->静态化后,在类加载的时候,会将其加载到内存的方法区进行共享
2、单例设计模式---->保证只对一个实例进行操作。
3、将资源作为操作该资源的类的构造函数的参数,这样可以保证此类的多个对象在使用该资源的时候使用该资源的同一个实例。
现在我们要用第三种方法来进行线程间的通信。
情景:两个线程 ,一个负责输入,一个负责输出;共同处理一个资源。

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 Input implements Runnable{
        Resource r;
        Input(Resource r){
            this.r = r;
        }
        @Override
        public void run() {
            while(true){
                if(x == 0){
                    r.setName("小明");
                    r.setSex("女");
                }else{
                    r.setName("snoopy");
                    r.setSex("man");
                }
                x = (x+1)%2;
            }
        }
    }
    class Output implements Runnable{
        Resource r;
        Output(Resource r){
            this.r = r;
        }
         @Override
        public void run() {
            while(true){
                System.out.println(r.getName()+":             "+r.getSex());
            }
        }
    }
    class Resource{
        private String name;
        private String sex;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getSex() {
            return sex;
        }
        public void setSex(String sex) {
            this.sex = sex;
        }
    }

结果出现了下列情况:
snoopy: man
小明: man
小明: man
snoopy: 女
小明: man
小明: man
snoopy: 女
小明: man
这说明了线程发生了安全问题。
将操作共享资源的代码加上锁:

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 Input implements Runnable{
        Resource r;
        Input(Resource r){
            this.r = r;
        }
        @Override
        public void run() {
            while(true){
                synchronized(r){
                    if(x == 0){
                        r.setName("小明");
                        r.setSex("女");
                    }else{
                        r.setName("snoopy");
                        r.setSex("man");
                    }
                    x = (x+1)%2;
                }
            }
        }
    }
    class Output implements Runnable{
        Resource r;
        Output(Resource r){
            this.r = r;
        }
         @Override
        public void run() {
            while(true){
                synchronized(r){
                    System.out.println(r.getName()+":             "+r.getSex());
                }
            }
        }
    }
    class Resource{
        private String name;
        private String sex;
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getSex() {
            return sex;
        }
        public void setSex(String sex) {
            this.sex = sex;
        }
    }

解决了线程安全问题。
线程间通信----等待唤醒机制
  上述情况是没有顺序的,我们的目的是输入一个,输出一个,那么我们在进行输入的时候就要进行处理:
    1、当资源有值但是未输出的情况下就不能再输入。 ----等待
    2、当资源已经输出的时候就要继续输入。 ----唤醒
  等待唤醒涉及方法:
  wait():让线程处于冻结状态,被wait的线程都会被存储到线程池内。
  在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。
  当前线程必须拥有此对象监视器。
  该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。
  然后该线程将等到重新获得对监视器的所有权后才能继续执行。
  notify():唤醒线程池中的任意一个线程。
  唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。
  选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
  notifyAll():唤醒线程池中所有线程。
  唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
  这些方法都必须定义在同步中,因为这些方法都是用于操作线程状态的方法。必须要明确到底操作的是哪个锁上面的线程。
  重点:
    在使用上述方法的时候,一定要明确该方法所属的锁。
    换句话说:到底wait的是那个锁上的线程,如果是wait的A锁上面线程,那么就必须用A锁上的notify唤醒A锁上门的线程。

    为什么操作线程的方法定义在Object类中??
    因为这些方法是监视器的方法,而监视器其实就是锁。而锁可以是任意对象。任意对象都可以调用的方法一定在Object中。
  理解同步块和对象锁:
  1、我们可以把共享资源想象成为一个房间,但是这个房间上面可能不只是一个锁孔。但是我们可以想象每个锁孔都能独立的打开这扇门。
  2、synchronized 就是相当于这扇门的一个监视器,传进去的对象就是针对于某一个锁孔的一类钥匙,这类钥匙有多个备份。
  3、如果某一类锁中的一个打开了门,并进入房间操作共享资源,那么对于这一类钥匙的锁关闭,其他同类锁的钥匙打不开该门。
  4、当发生了线程等待的时候,针对于当前钥匙的主人进入线程池等待。直到也有该类钥匙的另外的人唤醒线程池池排队等候的有同类钥匙的人的时候,线程池会随机弄醒一位。
  5、如果是不同锁对象进行的操作,是没有同步作用的。
实例:

  

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 Input implements Runnable{
        Resource r;
        Input(Resource r){
            this.r = r;
        }
        @Override
        public void run() {
            int x = 0;
            while(true){
                synchronized(r){
                    if(r.isFlag() == false){
                        if(x == 0){
                            r.setName("小红");
                            r.setSex("女");
                        }else{
                            r.setName("snoopy");
                            r.setSex("man");
                        }
                        x = (x+1)%2;
                        r.setFlag(true);
                        r.notify();
                    }else{
                        try {
                            r.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    class Output implements Runnable{
        Resource r;
        Output(Resource r){
            this.r = r;
        }
         @Override
        public void run() {
            while(true){
                synchronized(r){
                    if(r.isFlag() == true){
                        System.out.println(r.getName()+":   "+r.getSex());
                        r.setFlag(false);
                        r.notify();
                    }else{
                        try {
                            r.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    class Resource{
        private String name;
        private String sex;
        private boolean flag = false;
        public boolean isFlag() {
            return flag;
        }
        public void setFlag(boolean flag) {
            this.flag = flag;
        }
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public String getSex() {
            return sex;
        }
        public void setSex(String sex) {
            this.sex = 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();
        }
    }
    class Input implements Runnable{
        Resource r;
        Input(Resource r){
            this.r = r;
        }
        @Override
        public void run() {
            int x = 0;
            while(true){
                synchronized(r){
                    if(x == 0){
                        r.in("小红", "女");
                    }else{
                        r.in("snoopy","man");
                    }
                    x = (x+1)%2;
                }
            }
        }
    }
    class Output implements Runnable{
        Resource r;
        Output(Resource r){
            this.r = r;
        }
         @Override
        public void run() {
            while(true){
                synchronized(r){
                    r.out();
                }
            }
        }
    }
    class Resource{
        private String name;
        private String sex;
        private boolean flag = false;
        public void in(String name,String sex){
            if(flag == false){
                this.name = name;
                this.sex = sex;
                flag = true;
                this.notify();
            }else{
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        public void out(){
            if(flag == true){
                System.out.println(this.name+":   "+this.sex);
                flag = false;
                this.notify();
            }else{
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
时间: 2024-10-11 07:17:50

多线程学习笔记四--------------线程间通信问题的相关文章

PyQt5学习笔记13----pyqt线程间通信

信号(singal)与槽(slot)用于对象相互通信,信号:当某个对象的某个事件发生时,触发一个信号,槽:响应指定信号的所做的反应,其实信号槽类似于.NET里面的委托.事件,比如Repeater控件类,当行数据绑定后,触发一个ItemDataBound事件,不管使用者使用会监听该事件并做额外处理,其控件类内部都会触发该事件,这种机制很多程度提高了类的封装性和完整性. PyQt的窗体控件类已经有很多的内置信号,开发者也可以添加自己的自定义信号,信号槽有如下特点: - 一个信号可以连接到许多插槽.

黑马程序员——JAVA基础之Day24 多线程 ,死锁,线程间通信 ,线程组,线程池,定时器。

------- android培训.java培训.期待与您交流! ---------- Lock()实现提供了比使用synchronized方法和语句可获得更广泛的锁定操作. private Lock lock =new ReentrantLock(); 被锁的代码要用   lock.lock()                lock.unlock()    包括.其中用try   ...finally包围 同步:效率低,如果出现同步嵌套,会出现死锁.  但是安全. 死锁问题:两个或者两个以上

Java笔记七.线程间通信与线程生命的控制

线程间通信与线程生命的控制 一.线程通信方法 Java是通过Object类的wait.notify.notifyAll这几个方法来实现进程键的通信.由于所有的类都是从Object继承的,因此在任何类中都可以直接使用这些方法. wait:告诉当前线程放弃监视器并进入睡眠状态,知道其他线程进入同一监视器并调用notify为止; notify:唤醒同一对象监视器中调用wait的第一个线程.用于类似饭馆有一个空位后通知所有等候就餐的顾客中的第一位可以入座的情况: notifyAll:唤醒同一对象监视器中

Java多线程之死锁与线程间通信简单案例

死锁定义 死锁是指两个或者多个线程被永久阻塞的一种局面,产生的前提是要有两个或两个以上的线程,并且来操作两个或者多个以上的共同资源:我的理解是用两个线程来举例,现有线程A和B同时操作两个共同资源a和b,A操作a的时候上锁LockA,继续执行的时候,A还需要LockB进行下面的操作,这个时候b资源在被B线程操作,刚好被上了锁LockB,假如此时线程B刚好释放了LockB则没有问题,但没有释放LockB锁的时候,线程A和B形成了对LockB锁资源的争夺,从而造成阻塞,形成死锁:具体其死锁代码如下:

Java多线程-管道流实现线程间通信

管道流 在Java语言中提供了各种各样的输入/输出流 Stream ,使我们能够很方便地对数据进行操作,其中管道流是一种特殊的流,用于在不同线程间直接传送数据.一个线程发送数据到输出管道流,另一个线程从输入管道流中读取数据. 通过使用管道,实现不同线程间的通信,而无须借助于类似临时文件之类的东西. 字节流 PipedInputStream 和 PipedOutputStream 字符流 PipedReader 和 PipedWriter 示例: public class PipeStreamTe

多线程学习笔记六-------------线程的消亡以及一些常用方法的介绍

多线程wait()和sleep()的区别: 1.wait()可以指定时间,sleep()必须指定时间. 2.在同步中时,对cpu的执行权和处理不同. wait() 释放执行权,释放锁. sleep() 释放执行权,不释放锁. 在同步锁中(synchronized),多线程可能同时进入锁中(多生产者多消费者问题),都被wait(),此时这些线程都释放了执行权并且释放了锁. 当有notifyAll()将其唤醒的时候,这些线程都获得了执行权,只有当执行notifyAll()的线程执行完同步锁内的内容的

生产者消费者中多线程安全问题(即线程间通信的安全问题)

一个生产线程,一个消费线程,程序不存在安全问题 两个生产线程,两个消费线程,出现了生产两次,消费一次或者生产一次,消费两次的情况. 出现问题的原因是:线程被唤醒之后没有回去判断标记,直接继续执行后边的代码. 解决方式:让线程被唤醒之后不是直接执行后边的代码,而是回去判断标志,这个问题可以通过把if改成while实现.          但这样做会出现死锁的状况,原因是唤醒了本方线程,导致所有线程全部等待.          notify()唤醒的是任意一个线程,不能保证唤醒的是对方线程,这个问题

JAVA多线程学习笔记<3>线程传入参数与终止线程方法

public class TimeThread extends Thread { private long start; public volatile boolean exit = false; public TimeThread(long start) { this.start = start; } public void run() { while(!exit) { long end = System.currentTimeMillis(); long useTime = end - st

2016年4月24日_JAVA学习笔记_多线程三_线程间通信

1.毕老师第十四天内容,线程间的通信.大概是使用wait(),notify()等一系列函数来控制各个线程的CPU执行资格和执行权,通过合适的时机在各个线程当中切换来达到线程间通信的目的. 涉及到的方法: wait():让线程处于等待状态,被wait()的线程会被存储到线程池当中,直到被唤醒.只能在同步方法中被调用. notify():随机选择一个在该对象上调用wait方法的线程,解除其阻塞状态.只能在同步方法和同步代码块中被调用. notifyAll():接触所有在该对象上调用wait()方法的