Java学习笔记46(多线程三:线程之间的通信)

多个线程在处理同一个资源,但是线程的任务却不相同,通过一定的手段使各个线程能有效地利用资源,

这种手段即:等待唤醒机制,又称作线程之间的通信

涉及到的方法:wait(),notify()

示例:

两个线程一个输入,一个输出

package demo;

public class Resource {
    public String name;
    public String sex;
}

输入线程:

package demo;

public class Input implements Runnable {
    private Resource r = new Resource();

    public void run() {
        int i = 0;
        while (true) {
            if (i % 2 == 0) {
                r.name = "张三";
                r.sex = "男";
            } else {
                r.name = "李四";
                r.sex = "女";
            }
            i++;
        }
    }

}

输出线程:

package demo;

public class Output implements Runnable {
    private Resource r = new Resource();
    public void run(){
        while (true) {
            System.out.println(r.name+"..."+r.sex);
        }
    }
}

测试类:

package demo;

public class ThreadDemo {
    public static void main(String[] args) {
        Input in = new Input();
        Output out = new Output();
        Thread tin = new Thread(in);
        Thread tout = new Thread(out);

        tin.start();
        tout.start();
    }
}

运行后却发现输出的都是null...null

因为输入线程和输出线程中创建的Resource对象使不同的

解决null问题:

package demo;

public class Input implements Runnable {
    private Resource r;

    public Input(Resource r){
        this.r = r;
    }

    public void run() {
        int i = 0;
        while (true) {
            if (i % 2 == 0) {
                r.name = "张三";
                r.sex = "男";
            } else {
                r.name = "李四";
                r.sex = "女";
            }
            i++;
        }
    }

}
package demo;

public class Output implements Runnable {
    private Resource r;

    public Output(Resource r){
        this.r = r;
    }

    public void run(){
        while (true) {
            System.out.println(r.name+"..."+r.sex);
        }
    }
}
package demo;

public class ThreadDemo {
    public static void main(String[] args) {

        Resource r = new Resource();

        Input in = new Input(r);
        Output out = new Output(r);
        Thread tin = new Thread(in);
        Thread tout = new Thread(out);

        tin.start();
        tout.start();
    }
}

运行后又发现了另一个问题:

输出中含有:张三...女或者李四...男,性别出错

发生原因:

赋值完张三和男后,继续赋值李四和女,这时候还未还得及赋值女,就进入了输出线程,这时候就会输出李四...男

于是想到加上同步:

    public void run() {
        int i = 0;
        while (true) {
            synchronized (this) {
                if (i % 2 == 0) {
                    r.name = "张三";
                    r.sex = "男";
                } else {
                    r.name = "李四";
                    r.sex = "女";
                }
                i++;
            }
        }
    }
    public void run() {
        while (true) {
            synchronized (this) {
                System.out.println(r.name + "..." + r.sex);
            }
        }
    }

然而问题并没有解决:

原因:

这里的同步失去了作用,用到的不是一个锁

解决办法:

使用一个共同的锁即可

public void run() {
        int i = 0;
        while (true) {
            synchronized (r) {
                if (i % 2 == 0) {
                    r.name = "张三";
                    r.sex = "男";
                } else {
                    r.name = "李四";
                    r.sex = "女";
                }
                i++;
            }
        }
    }
    public void run() {
        while (true) {
            synchronized (r) {
                System.out.println(r.name + "..." + r.sex);
            }
        }
    }

这时候就是正常的输出了

但是还是存在一个问题,我们希望的是张三和李四交错出现,一个张三一个李四,现在依然是随机出现的,大片的张三或李四

解决办法:

先让input线程赋值,然后让output线程输出,并且让输入线程等待,不允许再赋值李四,等待输出张三结束后,再允许李四赋值,依次下去

输入线程也需要同样的方式,输出完后要等待

这时候就需要用到等待唤醒机制:

输入:赋值后,执行方法wait()永远等待

输出:打印后,再输出等待之前,唤醒输入notify(),自己再wait()永远等待

输入:被唤醒后,重新赋值,必须notify()唤醒输出的线程,自己再wait()等待

依次循环下去

代码实现:

package demo;

public class Resource {
    public String name;
    public String sex;
    public boolean flag = false;
}
package demo;

public class Input implements Runnable {
    private Resource r;

    public Input(Resource r) {
        this.r = r;
    }

    public void run() {
        int i = 0;
        while (true) {
            synchronized (r) {
                if (r.flag) {
                    try {
                        r.wait();
                    } catch (Exception e) {
                    }
                }
                if (i % 2 == 0) {
                    r.name = "张三";
                    r.sex = "男";
                } else {
                    r.name = "李四";
                    r.sex = "女";
                }
                r.flag = true;
                r.notify();
            }
            i++;
        }
    }
}
package demo;

public class Output implements Runnable {
    private Resource r;

    public Output(Resource r) {
        this.r = r;
    }

    public void run() {
        while (true) {
            synchronized (r) {
                if (!r.flag) {
                    try {
                        r.wait();
                    } catch (Exception e) {
                    }
                }
                System.out.println(r.name + "..." + r.sex);
                r.flag = false;
                r.notify();
            }
        }
    }
}
package demo;

public class ThreadDemo {
    public static void main(String[] args) {

        Resource r = new Resource();

        Input in = new Input(r);
        Output out = new Output(r);
        Thread tin = new Thread(in);
        Thread tout = new Thread(out);

        tin.start();
        tout.start();
    }
}

这时候就是张三李四交错输出了

完成

原文地址:https://www.cnblogs.com/xuyiqing/p/8320488.html

时间: 2024-12-22 10:15:37

Java学习笔记46(多线程三:线程之间的通信)的相关文章

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

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

疯狂Java学习笔记(62)------------线程初识

线程初识 没有线程编程的程序好比一个人一只手干活,而多线程的程序就好比一个人多只手干活. 进程是系统资源分配的独立单位,而线程是可调度运行的独立单位,一个进程可以拥有多个线程,线程是进程并行完成的多个任务. 进程.线程.并发执行 首先我们先来认识一下进程.线程.并发执行的概念: 一般来说,当运行一个应用程序的时候,就启动了一个进程,当然有些会启动多个进程.启动进程的时候,操作系统会为进程分配资源,其中最主要的资源是内存空间,因为程序是在内存中运行的. 在进程中,有些程序流程块是可以乱序执行的,并

Java学习笔记之多线程二

看到一篇讲线程的故事性文章,觉得很有意思,很佩服作者能这么生动地讲述出来,点击可跳转阅读此文章:<我是一个线程> 继续我的笔记中总结 - - 理解线程安全问题: 下面是书上看到的卖票例子:模拟3个窗口同时在售10张票. 上篇博文笔记总结了多线程创建的两种方式,那我们就分别以这两种实现多线程的方式来解决这个场景. 使用继承于Thread类的方式 上Demo: class SaleTicket extends Thread { int num = 10; // 票数 public SaleTick

Java学习笔记-8.多线程编程

一.引入线程 1.多线程和多进程的区别 (1)两者粒度不同,进程是由操作系统来管理,而线程则是在一个进程内 (2)每个进程是操作系统分配资源和处理器调度的基本单位,拥有独立的代码.内部数据和状态 而一个进程内的多线程只是处理器调度的基本单位,共享该进程的资源,线程间有可能相互影响 (3)线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担小 2.Thread类:Java的线程是通过java.lang.Thread类来实现,一个Thread对象代表一个线程

疯狂Java学习笔记(64)------------线程转换

线程的生命周期 由上图可以看出: 线程的状态转换是线程控制的基础.线程状态总的可分为五大状态:分别是生.死.可运行.运行.等待/阻塞. 各个状态细节不再赘述,具体请看http://blog.csdn.net/u011225629/article/details/46288995此篇博文 此篇博文主要讲述各个状态的实例! 一.新建和就绪状态 此处不做细致的讲解,在上一篇博文中已经讲解过了! package com.haixu.thread2; public class InvokeRun exte

疯狂Java学习笔记(65)---------线程协作

线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者模型:当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对临界资源(即队列)的占用权.因为生产者如果不释放对临界资源的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去.因此,一般情况下,当队列满时,会让生产者交出对临界资源的占用权,

java之等待唤醒机制(线程之间的通信)

线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同.比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,那么线程A与线程B之间就存在线程通信问题. 为什么要处理线程间通信: 多个线程并发执行时, 在默认情况下CPU是随机切换线程的,当我们需要多个线程来共同完成一件任务,并且我们希望他们有规律的执行, 那么多线程之间需要一些协调通信,以此来帮我们达到多线程共同操作一份数据. 如何保证线程间通信有效利

JAVA学习笔记之多线程专题(一):线程同步安全处理

关于多线程操作,我相信大家都不陌生,如何开启一个线程之类我想就不用太详细的去描述,今天我们就来讲讲线程同步的安全的问题. 对于线程同步安全问题,一般是一个多线程对同一个资源同时操作的时候,会出现资源同时操作造成线程不安全的问题.那么这个时候我们需要去对公共资源进行同步保护.这个时候有三种情况 1.同步代码块,这个同步的锁是任意一个对象: 2.方法同步,这个同步的锁就是该方法所在的类: 3.静态方法同步,这个同步的锁是该方法所在类的字节码. 接下来,我们举一个例子来说明多线程对同一个资源进行操作的

Java学习笔记之多线程

/* 进程: 正在进行中的程序(直译). 线程: 就是进程中一个负责程序执行的控制单元(执行路径) 一个进程中可以有多个执行路径, 称之为多线程. 一个进程中至少要有一个线程. 开启多个线程是为了同时运行多部分代码. 每一个线程都有自己运行的内容. 这个内容可以称为线程要执行的任务. 多线程的好处: 解决了多部分同时运行的问题. 多线程的弊端: 线程太多回到效率的降低. 其实应用程序的执行都是cpu在做着快速的切换完成的. 这个切换是随机的. jvm启动时就启动了多个线程,至少有两个线程可以分析