Java 多线程初探(二) - 通讯与协调
Java中线程共有5中状态:
- 新建:当创建一个线程对象时,新线程对象就处于新建状态,并获得除CPU外所需的资源。
- 就绪:当处于新建状态的线程被启动后,将进入线程队列等待CPU资源。这时它就具备了运行条件,一旦获得CPU资源,就可以脱离创建它的主线程独立运行。另外原来处于阻塞状态的线程结束阻塞状态后,也进入就绪状态。
- 运行:当一个就绪状态的线程获得CPU资源时,就进入了运行状态。每个线程对象都有一个run方法,一旦线程开始运行,就会自动运行该方法。run()中定义了具体的线程操作。
- 阻塞:一个正在运行的线程因为某种特殊的原因,比如遇到优先级高的线程、某种资源无法满足时就会让出CPU并停止自身的运行,进入阻塞状态。只有当引起阻塞的原因消除时,它才能重新进入就绪状态。
- 死亡:不具备继续运行能力的线程处于死亡状态。一般由两种情况引起:run()方法运行完毕;其他线程强制终止它。
就绪队列:处于就绪状态的线程都在就绪队列中等待CPU资源,而一般就绪队列中会有多个线程。为此,系统会给每一个线程分配一个优先级,优先级高的可以排在较前面的位置,能优先得到CPU资源。对于优先级相同的线程,一般按照先来先服务的原则调度。
进程和线程最大的区别在于进程是由操作系统来控制的,而线程是由进程来控制的,所以是由程序员来控制的。进程都是相互独立的,各自享有各自的内存空间。而一个进程中的多个线程是共享内存空间的,他们可以访问相同的变量和对象。本来这样的设计方便了线程间的通讯,但却带来了新的问题:多个线程同时访问一个变量可能出现意想不到的错误,如死锁。所以多线程操作要注意协调与配合,进行互斥和同步处理。
- 互斥:当多个线程需要访问同一资源时,而这一资源在某一时刻只允许一个线程访问,那么这些线程就是互斥。如线程A需要读取变量count,而线程B会给count赋值,那边A和B是互斥的。
- 同步:多个线程需要访问同一资源,而且需要相互配合才能正常工作,这些线程运行时就是一种同步关系。例如,A线程需要从缓冲区中读取数据,而B线程需要向缓冲区中写数据,但缓冲区的大小是固定的。
- 临界区:为了实现线程间的互斥和同步,需要将共享资源放入一个区域,该区域一次只允许一个线程进入,该区域就被称为临界区域。线程在访问共享资源前需要进行检查,看自己是否有权访问资源,如果有权访问还要阻止其他线程进入该区域。该代码段就是临界区。
- 死锁:若多个线程相互等待其他线程释放资源,而所有线程又都不释放自己所占的资源,从而导致相关线程处于永远等待的状态,这种现象称为线程死锁。
线程通讯
线程间互斥和同步必须使用信号量,Java中信号量需要用户自己管理,系统只提供了起到PV原语作用的方法:
- void notify():唤醒在此对象监视器上等待的某一个线程.具体是哪一个可以认为是不确定的.
- void notifyAll():唤醒在此对象监视器上等待的所有线程。
- void wait():导致当前的线程等待,直到其他线程调用此对象的 notify()方法或 notifyAll()方法.
wait会释放占有的资源(锁),notify和notifyAll不会释放占用的资源(锁).而线程睡眠(sleep)时,也都不会释放资源(锁)。
除了三个方法,还有一个常用的关键字:synchronized.
凡是被synchronized标志的资源,都是临界区,在同一时刻只能有一个线程能够进入该临界区。synchronized可以用于数据、方法,甚至是一段代码。synchronized也被称为“对象锁”,而且上面的三个方法都只能使用在由synchronized标志的代码块中,否则会发生如下异常:
java.lang.IllegalMonitorStateException: current thread not owner
synchronized是和一个锁对象关联的,一个对象只有一个锁。当程序运行到synchronized同步方法或代码块时就会检查锁,如果一个线程获得该锁,就没有其他线程可以获得锁,直到第一个线程释放(或返回)锁。这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放。
如myThread.java文件代码:
public class myThread extends Thread {private static int count=0;private static Object o=new Object();//定义一个Object对象,用于锁 @Overridepublic void run() {synchronized(o){for(int i=0;i<20;i++){count+=1;System.out.println("My name is "+this.getName()+" count= "+count); try {sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}}public myThread(String arg0) {super(arg0);} }
启动线程代码如下:
myThread tf=new myThread("first"); myThread ts=new myThread("secod"); tf.start(); ts.start();
执行的结果:
My name is secod count= 1 My name is secod count= 2 ..... ..... My name is secod count= 20 My name is first count= 21 My name is first count= 22 My name is first count= 23 ..... ..... My name is first count= 40
myThread.java文件中的for循环使用synchronized标志,是临界区,第一次进入的是secod线程,知道执行完毕释放锁,first线程才进入临界区。
synchronized可以用于方法、数据,甚至是代码块:
- synchronized限制static方法的时候,锁对象为这个类的class变量,相当于XXXClass.class. 例如:
public static synchronized int setName(String name){Xxx.name = name; }
等价于:
public static int setName(String name){synchronized(Xxx.class){Xxx.name = name;} }
- synchronized限制非static方法的时候,锁对象为这个类的实例(相当于this).例如:
public synchronized int getX() {return x++; }
等价于:
public int getX() {synchronized (this) {return x;} }
- synchronized限制一个对象实例的时候,如(synchronized (xlock)),锁对象为指定的这个对象实例,如xlock.
synchronized限制static方法的时候,在某一时刻,同一个虚拟机只能有一个线程正在执行这个static方法,因为锁对象是这个class的XXXClass.class实例,一个虚拟机只有一个XXXClass.class实例
如果把myThread.java文件代码改成如下:
public class myThread extends Thread {private static int count=0;private static Object o=new Object();//定义一个Object对象,用于锁 @Overridepublic synchronized void run() {for(int i=0;i<20;i++){count+=1;System.out.println("My name is "+this.getName()+" count= "+count); try {sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}public myThread(String arg0) {super(arg0);} }
执行的结果:
My name is first count= 1 My name is secod count= 2 My name is secod count= 3 My name is first count= 4 My name is first count= 5 My name is secod count= 6 My name is secod count= 7 ..... ..... My name is secod count= 34 My name is first count= 35 My name is secod count= 36 My name is secod count= 37 My name is first count= 38 My name is first count= 39 My name is secod count= 40
可见是交叉执行的,那是因为synchronized限制某一个类的非static方法的时候,对这个类的某一特定实例,在某一时刻,同一个虚拟机只能有一个线程正在执行这个方法,但是可以同时执行多个实例的这个特定方法,因为锁对象不同.
转载于:https://www.cnblogs.com/zhaiqianfeng/archive/2012/08/17/4617055.html
Java 多线程初探(二) - 通讯与协调相关推荐
- Java多线程入门二
Java多线程入门二 基类 package com.cv.DuoXianCheng;import lombok.extern.slf4j.Slf4j;@Slf4j public class Hero ...
- Java总结篇系列:Java多线程(二)
本文承接上一篇文章<Java总结篇系列:Java多线程(一)>. 四.Java多线程的阻塞状态与线程控制 上文已经提到Java阻塞的几种具体类型.下面分别看下引起Java线程阻塞的主要方法 ...
- java多线程间的通讯
什么是多线程之间的通讯? 就是多个线程在操作同一个资源,但是操作的动作不同. package com;class Printer{// 打印机public String fileName;public ...
- Java多线程系列(二):线程的五大状态,以及线程之间的通信与协作
在Java面试的时候,经常会问到Java并发编程相关的多线程.线程池.线程锁.线程通信等面试必考点,比如: Java并发编程系列:Java线程池的使用方式,核心运行原理.以及注意事项 Java并发编程 ...
- Java多线程(二)之Atomic:原子变量与原子类
一.何谓Atomic? Atomic一词跟原子有点关系,后者曾被人认为是最小物质的单位.计算机中的Atomic是指不能分割成若干部分的意思.如果一段代码被认为是Atomic,则表示这段代码在执行过程中 ...
- Java多线程学习(二)---线程创建方式
线程创建方式 摘要: 1. 通过继承Thread类来创建并启动多线程的方式 2. 通过实现Runnable接口来创建并启动线程的方式 3. 通过实现Callable接口来创建并启动线程的方式 4. 总 ...
- Java多线程(二)——多线程基本特性
目录 一.引言 二.优先级 三.睡眠sleep 四.加入线程join 五.礼让线程yield 六.守护线程daemon 七.中断线程 八.总结 一.引言 在jdk1.5之前多线程有很多基础的功能,下面 ...
- java多线程学习二、安全与不安全示例:12306买票和银行取钱、java内存模型、内存可见性、线程同步块和方法
文章目录 前言 1. 什么是块,分为几种 2. 静态块与构造块的区别 一. 举例说明:并发情况下,线程不安全 1. 示例1:unsafe12306取票 2. 示例2:unsafe银行取钱 二.线程不安 ...
- Java多线程(二):Thread类
Thread类的实例方法 start() start方法内部会调用方法start方法启动一个线程,该线程返回start方法,同时Java虚拟机调用native start0启动另一个线程调用run方法 ...
最新文章
- JAVA架构师面试题and如何成为架构师
- java 方法执行结束局部变量释放_JAVA-方法在执行过程中,JVM的内存分配和变化情况,栈(stack)的情况浅析...
- html切换选择项,HTML选择选项元素
- centos7下php设置用户和组,centos系统添加/删除用户和用户组的例子
- 关于 Node.js 参数 max-old-space-size
- 马云狂炸近百亿,你的借呗额度涨了吗?
- Java技术:为什么不推荐使用BeanUtils属性转换工具
- iOS开发者《用2块钱快速创建你的网站或博客》
- linux邮件客户端软件,适用于Linux的4款最佳电子邮件客户端 | MOS86
- tanh函数matlab_MATLAB模拟双曲正切函数tanh和双曲余切函数coth
- 用python算24点及原理详解
- DBLE分库分表中间件
- 工作那些事(二十八)项目管理模式:项目型、职能型、矩阵型
- 什么是变量?变量的本质是什么?变量的生命周期和作用域
- 区块链≠分布式账本,别再傻傻分不清
- 不用找了,这300家公司面试不考算法
- 九连环的递归实现,以及数列通项
- 正则表达式口诀 正则表达式学习工具
- Java多数据源最通俗讲解
- 虚拟化系列-VMware vSphere 5.1 VDP备份管理
热门文章
- 算法:合并两个有序链表
- MySQl笔记8:把good表中商品名为‘诺基亚xxxx‘的商品,改为‘HTCxxxx‘
- 安装NFS服务,并挂载到开发板
- 常考数据结构与算法-NC105 二分查找-II
- 史上最全,最完美的 JAVA 技术体系思维导图总结,没有之一!
- 汇编:call指令的应用
- 移动端开发 rem 单位使用问题
- 【前端词典】和媳妇讲代理后的意外收获
- 【Visual Studio】如何在VS 2012中打印变量值到输出窗口
- Sub-process /usr/bin/dpkg returned an error code (1)