java多线程编程学习(线程间通信)

一、概要

线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通信就是成为整体的必用方案之一。可以说,使线程进行通信后,系统之间的交互性会更强大,在大大提高cpu利用率的同时还会使程序员对各线程任务在处理过程中进行有效的把控和监督。

二、等待/通知机制

1、"wait/notify"机制:等待/通知机制,wait使线程暂停运行,而notify 使暂停的线程继续运行。用一个厨师和服务员的交互来说明:

(1) 服务员取到菜的时间取决于厨师,所以服务员就有“等待”(wait)的状态。

(2) 厨师将菜放在“菜品传递台”上,其实就相当于一种通知(notify),这时服务员才可以拿到菜并交给就餐者。

2、wait()

(1) 使当前执行代码的线程进行等待。wait()方法是Object类的方法,该方法用来将当前线程置入“预执行队列”中,并且在wait()所在的代码行处停止执行,直到接收通知或被中断为止。

(2) 在调用wait()方法之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法,否则抛出IllegalMonitorStateException异常。(属于Runtime的一个子类,不需要try-catch 语句进行捕捉异常)

(3) 在调用wait()方法之后,当前线程释放锁,而此对象会进入线程等待池中,等待被唤醒。在从wait()返回前,线程与其他呈wait线程竞争重新获得锁。

(4) wait()方法可以被interrupt 打断并抛出InterruptedException。

(5) wait(long):带一个参数的wait(long)方法的功能是等待某一时间内是否有线程对锁进行唤醒,如果超过这个时间则自动唤醒。

3、notify()

(1) 用来通知那些可能等待该对象的对象锁的其他线程。如果有多个线程等待,则由线程规划器随机挑选出其中一个呈wait状态的线程,对其发出通知notify,并使它等待获取该对象的对象锁。(注意!这里说的是等待,即在执行完notify()方法后,当前线程不会马上释放该对象锁,即wait()状态的线程也不会马上获得对象锁,需要将synchronized 代码块中的代码执行完后才释放锁!)

(2)也要在同步方法或同步块中调用,即在调用前,线程也必须获得该对象的对象级别锁,否则也会抛出IllegalMonitorStateException.

(3)当notify()发出通知,却没有wait()线程在等待时,则不作作用。

4、notifyAll()

(1) 可以使所有正在等待队列中的 等待同一共享资源(即同一个锁) 的"全部"线程从等待状态退出,进入可运行状态。

5、

6、假死:“假死”现象其实就是线程进入WAITING等待状态。如果全部线程都进入WAITING状态,则程序就不再执行任何功能了,整个项目呈停止状态。 出现这样的原因是因为:比如多个生产者和多个消费者的问题,“生产者”可能唤醒“生产者”,“消费者”可能唤醒“消费者”,唤醒了同类,导致线程不断在等待。怎么解决这个问题呢?将notify() 改成 notifyAll()方法即可,也就是将异类一同唤醒就可以了。

7、 Jave中 管道流(pipeStream)是一种特殊的流,可用于在不同的线程中直接传送数据。一个线程发送数据到输出管道,另一个线程从输入管道中读数据。通过使用管道,实现不同线程间的通信,而无须借助于类似临时文件之类的东西。JDK中提供了四个类来使线程间可以通信,其中包括字节流(PipedOutputStream、PipedInputStream)和字符流(PipedWriter、PipedReader)。

public class Run {
 public static void main(String[] args) {
  try {
   WriteData writeData = new WriteData();
   ReadData readData = new ReadData();
   PipedOutputStream outputStream = new PipedOutputStream();
   PipedInputStream inputStream = new PipedInputStream();

   outputStream.connect(inputStream);//使两个Stream之间产生通信链接,这样才可以将数据进行输入输出

   ThreadRead threadRead = new ThreadRead(readData, inputStream);
   threadRead.start();
   Thread.sleep(1000);
   ThreadWrite threadWrite = new ThreadWrite(writeData, outputStream);
   threadWrite.start();
  } catch (IOException e) {
   e.printStackTrace();
  } catch (InterruptedException e) {
   e.printStackTrace();
  }
 }
}

三、方法join的使用

1、在很多情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时计算,主线程往往将早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束,比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join()方法了。

2、join()的作用是等待线程销毁,而使 当前线程进行无限期的阻塞,等待join()的线程销毁后再继续执行当前线程的代码。

3、同样的,join()方法可以被interrupt()方法打断并抛出InterruptedException异常。

4、join 与 synchronized 的区别?

(1) join()在内部使用wait() 方法进行等待。

(2) synchronized 关键字使用的是“对象监视器”原理作为同步。

5、方法join(long) 和 sleep(long)的区别?

(1) join(long)内部采用wait(long)方法实现,当执行wait(long)方法后,当前线程的锁被释放,那么其他线程也可以调用此线程中的同步方法了。即 join(long)之后,该线程释放锁,又需要和其他线程去争抢锁的资源。

(2) Thread.sleep(long)方法不释放锁。

四、类 ThreadLocal 的使用

1、变量值的共享可以使用public static 变量的形式,所有的线程都使用同一个public static 变量。如果想实现每一个线程都有自己的共享变量该如何解决呢?类ThreadLocal解决的就是每个线程绑定自己的值,可以将ThreadLocal类比喻成全局存放数据的盒子,盒子中可以存放每个线程的私有数据。

2、类ThreadLocal 具有隔离性,即每个线程都可以存入自己线程的数据而互不影响,而取到的也是自己线程存入的数据。

五、类InheritableThreadLocal的使用

1、InheritableThreadLocal类继承于ThreadLocal类,所以它具有ThreadLocal类的特性,但又是一种特殊的ThreadLocal,其特殊性在于InheritableThreadLocal变量值会自动传递给所有子线程,而普通ThreadLocal变量不行;而且,通过重写这个类中的 childValue 方法,子线程的值可以作为父线程值的一个任意函数。

备注:

(1) 什么是子线程?

包含在 Thread thread = new Thread(new ThreadStart(delegate{
}));里面均视为子线程。(个人理解)

(2) 什么是主线程?

UI界面和Main函数均为主线程,除了“不包含在Thread里面的程序”均可 视为主线程。(个人理解)

以上这篇java多线程编程学习(线程间通信)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

时间: 2017-09-11

Java多线程的用法详细介绍

Java多线程的用法详细介绍 最全面的Java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法. 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread构造函数: public Thread( ); publi

java多线程学习笔记之自定义线程池

当我们使用 线程池的时候,可以使用 newCachedThreadPool()或者 newFixedThreadPool(int)等方法,其实我们深入到这些方法里面,就可以看到它们的是实现方式是这样的. public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueu

java 中多线程生产者消费者问题详细介绍

java 中多线程生产者消费者问题 前言: 一般面试喜欢问些线程的问题,较基础的问题无非就是死锁,生产者消费者问题,线程同步等等,在前面的文章有写过死锁,这里就说下多生产多消费的问题了 import java.util.concurrent.locks.*; class BoundedBuffer { final Lock lock = new ReentrantLock();//对象锁 final Condition notFull = lock.newCondition(); //生产者监视

java 多线程Thread与runnable的区别

java 多线程Thread与runnable的区别 java中实现多线程的方法有两种:继承Thread类和实现runnable接口 1,继承Thread类,重写父类run()方法 public class thread1 extends Thread { public void run() { for (int i = 0; i < 10000; i++) { System.out.println("我是线程"+this.getId()); } } public static

java多线程之火车售票系统模拟实例

1.前言 为了学习多线程共享与通信,我们模拟一个火车售票系统,假设有10张火车票,三个窗口(也就是三个线程)同时进行售票. 2.非同步代码 package com.tl.skyLine.thread; /** * Created by tl on 17/3/6. */ public class SellTicket { public static void main(String[] args) { TicketWindow tw = new TicketWindow(); Thread t1

java 多线程的三种构建方法

java  多线程的三种构建方法 继承Thread类创建线程类 public class Thread extends Object implements Runnable 定义Thread类的子类,并重写其run()方法 创建Thread子类的实例,即创建了线程对象 调用线程对象的start()方法启动线程 public class FirstThread extends Thread { public void run(){ for(int i=0;i<100;i++){ /* * Thre

Java多线程用法的实例详解

Java多线程用法的实例详解 前言: 最全面的java多线程用法解析,如果你对Java的多线程机制并没有深入的研究,那么本文可以帮助你更透彻地理解Java多线程的原理以及使用方法. 1.创建线程 在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread构造函数: public Thread( ); p

Java Socket实现多线程通信功能示例

本文实例讲述了Java Socket实现多线程通信功能的方法.分享给大家供大家参考,具体如下: 前面的文章<Java Socket实现单线程通信的方法示例>说到怎样写一个最简单的Java Socket通信,但是文章中的例子有一个问题就是Server只能接受一个Client请求,当第一个Client连接后就占据了这个位置,后续Client不能再继续连接,所以需要做些改动,当Server没接受到一个Client连接请求之后,都把处理流程放到一个独立的线程里去运行,然后等待下一个Client连接请求

Java基于Socket实现简单的多线程回显服务器功能示例

本文实例讲述了Java基于Socket实现简单的多线程回显服务器功能.分享给大家供大家参考,具体如下: 需要两个类,一个是EchoServer,代表服务器.另外一个是EchoServerClient,代表客户端.代码如下: package interview; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter

java多线程学习之死锁的模拟和避免(实例讲解)

1.死锁 死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放.由于线程被无限期地阻塞,因此程序不可能正常终止. Java 死锁产生的四个必要条件: 1.互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用 2.不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放. 3.请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有. 4.循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的

【Java IO流】字节流和字符流的实例讲解

字节流和字符流 对于文件必然有读和写的操作,读和写就对应了输入和输出流,流又分成字节和字符流. 1.从对文件的操作来讲,有读和写的操作--也就是输入和输出. 2.从流的流向来讲,有输入和输出之分. 3.从流的内容来讲,有字节和字符之分. 这篇文章先后讲解IO流中的字节流和字符流的输入和输出操作. 一.字节流 1)输入和输出流 首先,字节流要进行读和写,也就是输入和输出,所以它有两个抽象的父类InputStream.OutputStream. InputStream抽象了应用程序读取数据的方式,即

Python 模拟购物车的实例讲解

1.功能简介 此程序模拟用户登陆商城后购买商品操作.可实现用户登陆.商品购买.历史消费记查询.余额和消费信息更新等功能.首次登陆输入初始账户资金,后续登陆则从文件获取上次消费后的余额,每次购买商品后会扣除相应金额并更新余额信息,退出时也会将余额和消费记录更新到文件以备后续查询. 2.实现方法 架构: 本程序采用python语言编写,将各项任务进行分解并定义对应的函数来处理,从而使程序结构清晰明了.主要编写了六个函数: (1)login(name,password) 用户登陆函数,实现用户名和密码

python3 模拟登录v2ex实例讲解

闲的无聊... 网上一堆,正好练手(主要是新手) # coding=utf-8 import requests from bs4 import BeautifulSoup headers = { 'user-agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36', 'origin': 'https://ww

Java 普通代码块静态代码块执行顺序(实例讲解)

如下所示: class B { public B() { super(); System.out.println("构造器B"); } { System.out.println("普通的代码块B"); } static{ System.out.println("静态代码块B"); } } public class ClassA extends B { public ClassA() { super(); System.out.println(&q

Vuejs2 + Webpack框架里,模拟下载的实例讲解

在实际的开发工作中,难免要配合销售人员,提前做一些前端的 DEMO 出来.这个时候往往还没有连接后端 API.假如要演示一个下载连接,那么应该如何做呢? 我们希望能够达成以下两点: 1.在开发环境下,我们可以在 webpack-dev-server 开发服务器上点击下载连接,点击后浏览器就能不下载文件. 2.当演示的时候,代码编译后放到 nginx 中.用户可以点击下载链接.nginx存放的都是业务代码. 那么如何做到这两点呢?假如我们要模拟下载一个 test.docx 文件.我们可以利用 ur

java接入创蓝253短信验证码的实例讲解

说明 项目是springboot框架 1.短信配置文件 包含验证码发送路径.用户名.密码 chuanglan.requesturl= chuanglan.account= chuanglan.pswd= 配置文件 具体值 查看官网 位置查看截图 红框已经标红 2.读取配置文件类 3.发送数据request实体类 public class SmsVariableRequest { private String account; private String password; private St

Java 多线程学习详细总结

目录(?)[-] 一扩展javalangThread类 二实现javalangRunnable接口 三Thread和Runnable的区别 四线程状态转换 五线程调度 六常用函数说明 使用方式 为什么要用join方法 七常见线程名词解释 八线程同步 九线程数据传递 本文主要讲了java中多线程的使用方法.线程同步.线程数据传递.线程状态及相应的一些线程函数用法.概述等. 首先讲一下进程和线程的区别: 进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1

哲学家就餐问题中的JAVA多线程学习

问题描述:一圆桌前坐着5位哲学家,两个人中间有一只筷子,桌子中央有面条.哲学家思考问题,当饿了的时候拿起左右两只筷子吃饭,必须拿到两只筷子才能吃饭.上述问题会产生死锁的情况,当5个哲学家都拿起自己右手边的筷子,准备拿左手边的筷子时产生死锁现象. 解决办法: 1.添加一个服务生,只有当经过服务生同意之后才能拿筷子,服务生负责避免死锁发生. 2.每个哲学家必须确定自己左右手的筷子都可用的时候,才能同时拿起两只筷子进餐,吃完之后同时放下两只筷子. 3.规定每个哲学家拿筷子时必须拿序号小的那只,这样最后