Java多线程系列(1)
本章主要内容有:
1.线程进程的区别
2.线程的生命周期
3.Java内存模型 原子性,可见性及有序性
4.线程池及Java实现
1. 线程进程的区别
- 线程:程序运行的最小单位
- 进程:资源分配的最小单位
- 一个进程可以有多个线程,多个线程共享进程里面的数据
- 线程间通讯相对更加方便,进程间通讯需要通过IPC(Inter-Process Communication,进程间通信)实现
- JVM运行时数据区域是否线程共享:
- 程序计数器-线程私有
- 虚拟机栈-线程私有
- 本地方法栈-线程私有
- 堆-线程共享
- 方法区-线程共享
3. 线程的生命周期
- 1.sleep()
- 在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。
- sleep()使当前线程进入等待状态,在指定时间内不会执行。
- 2.wait()
- 在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。
- 3.yield()
- 暂停当前正在执行的线程对象。
- yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
- yield()只能使同优先级或更高优先级的线程有执行的机会。
- 调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。
- 4.join()
- 等待调用join方法的线程结束,再继续执行。如:t.join();//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测。
4. Java线程几种创建方式
- 1. 实现Runnable或Callable接口
- 2. 实现Thread类
- 3. 通过线程池启动1,2创建的线程
5. Java内存模型(JMM)
- Java内存模型定义了线程和内存的交互方式,在JMM抽象模型中,分为主内存、工作内存;
- 主内存是所有线程共享的,一般是实例对象、静态字段、数组对象等存储在堆内存中的变量。
- 工作内存是每个线程独占的,线程对变量的所有操作都必须在工作内存中进行,
- 不能直接读写主内存中的变量,线程之间的共享变量值的传递都是基于主内存来完成。
1) JMM中的8个原子操作
- lock(锁定):作用于主内存的变量,把一个变量标识为一条线程独占状态。
- unlock(解锁):作用于主内存变量,把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定。
- read(读取):作用于主内存变量,把一个变量值从主内存传输到线程的工作内存中,以便随后的load动作使用
- load(载入):作用于工作内存的变量,它把read操作从主内存中得到的变量值放入工作内存的变量副本中。
- use(使用):作用于工作内存的变量,把工作内存中的一个变量值传递给执行引擎,每当虚拟机遇到一个需要使用变量的值的字节码指令时将会执行这个操作。
- assign(赋值):作用于工作内存的变量,它把一个从执行引擎接收到的值赋值给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作。
- store(存储):作用于工作内存的变量,把工作内存中的一个变量的值传送到主内存中,以便随后的write的操作。
- write(写入):作用于主内存的变量,它把store操作从工作内存中一个变量的值传送到主内存的变量中。
2) Happen-before规则
- Happens-before原则定义:
- 1. 如果A操作happens-before B操作,那么A操作的执行结果将对B操作可见,而且A操作的执行顺序排在B操作之前。
- 2. 两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。
- Happens-before规则(摘自《深入理解Java虚拟机第12章》):
- 1.程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;
- 2.锁定规则:一个unLock操作先行发生于后面对同一个锁的lock操作;
- 3.volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
- 4.传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
- 5.线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
- 6.线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
- 7.线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
- 8.对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;
- 例:A线程调用set, B线程调用同一个对象的get,那么B线程返回什么?
- private int i = 0;
- public void set(int j ){
- i = j;
- }
- public int get(){
- return i;
- }
- 如何修改满足Happen-before规则?
6. 原子性,可见性,有序性特征
- 原子性
- 即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。原子性就像数据库里面的事务一样,他们是一个团队,同生共死
- 可见性
- 是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值
- 有序性
- 即程序执行的顺序按照代码的先后顺序执行
- 解决方案:
- 原子性:
- Java中提供了两个高级指令 monitorenter和 monitorexit,也就是对应的synchronized同步锁来保证原子性,也可以通过lock(),unlock()对保证代码块的原子性
- 可见性:
- volatile、synchronized、final都可以解决可见性问题
- 有序性:
- synchronized和volatile可以保证多线程之间操作的有序性,volatile会禁止指令重排序
7. 线程池及Java实现
线程池及为什么使用线程池
- 在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程
- 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 第三:提高线程的可管理性。
Java线程池的参数
- ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
- BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
- RejectedExecutionHandler handler)
- 1.corePoolSize:
核心池的大小。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;- 2.maximumPoolSize:
线程池最大线程数。这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;- 3.keepAliveTime:
表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;- 4.unit:
参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性- 5.workQueue:
一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:ArrayBlockingQueue; LinkedBlockingQueue; SynchronousQueue; PriorityBlockingQueue- ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和SynchronousQueue。线程池的排队策略与BlockingQueue有关。
- 6.threadFactory:
线程工厂,主要用来创建线程- 7.handler:
表示当拒绝处理任务时的策略。有四种取值:- AbortPolicy:丢弃任务并抛出RejectedExecutionException异常;DiscardPolicy:也是丢弃任务,但是不抛出异常
- DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程);CallerRunsPolicy:由调用线程处理该任务。
线程在线程池中的处理:
1、如果线程池的当前大小还没有达到基本大小(poolSize < corePoolSize),那么就新增加一个线程处理新提交的任务;
2、如果当前大小已经达到了基本大小,就将新提交的任务提交到阻塞队列排队,等候处理workQueue.offer(command);
3、如果队列容量已达上限,并且当前大小poolSize没有达到maximumPoolSize,那么就新增线程来处理任务;
4、如果队列已满,并且当前线程数目也已经达到上限,那么意味着线程池的处理能力已经达到了极限,此时需要拒绝新增加的任务。至于如何拒绝处理新增
Java线程池的实现
- Executors.newFixedThreadPool(8)
- Executors.newCachedThreadPool()
- Executors.newSingleThreadExecutor()
- Executors.newScheduledThreadPool(8)
- Executors.newWorkStealingPool()
- Executors.newSingleThreadScheduledExecutor()
- 详见:https://www.cnblogs.com/barrywxx/p/8570558.html
- 问题:Executors.newCachedThreadPool()核心线程数设置为0,如何创建销毁线程的?
Java多线程系列(1)的更多相关文章
- Java多线程系列--“JUC锁”03之 公平锁(一)
概要 本章对“公平锁”的获取锁机制进行介绍(本文的公平锁指的是互斥锁的公平锁),内容包括:基本概念ReentrantLock数据结构参考代码获取公平锁(基于JDK1.7.0_40)一. tryAcqu ...
- Java多线程系列--“JUC锁”04之 公平锁(二)
概要 前面一章,我们学习了“公平锁”获取锁的详细流程:这里,我们再来看看“公平锁”释放锁的过程.内容包括:参考代码释放公平锁(基于JDK1.7.0_40) “公平锁”的获取过程请参考“Java多线程系 ...
- Java多线程系列--“JUC锁”10之 CyclicBarrier原理和示例
概要 本章介绍JUC包中的CyclicBarrier锁.内容包括:CyclicBarrier简介CyclicBarrier数据结构CyclicBarrier源码分析(基于JDK1.7.0_40)Cyc ...
- Java多线程系列--“JUC锁”02之 互斥锁ReentrantLock
本章对ReentrantLock包进行基本介绍,这一章主要对ReentrantLock进行概括性的介绍,内容包括:ReentrantLock介绍ReentrantLock函数列表ReentrantLo ...
- Java多线程系列--“JUC锁”01之 框架
本章,我们介绍锁的架构:后面的章节将会对它们逐个进行分析介绍.目录如下:01. Java多线程系列--“JUC锁”01之 框架02. Java多线程系列--“JUC锁”02之 互斥锁Reentrant ...
- Java多线程系列目录(共43篇)
最近,在研究Java多线程的内容目录,将其内容逐步整理并发布. (一) 基础篇 01. Java多线程系列--“基础篇”01之 基本概念 02. Java多线程系列--“基础篇”02之 常用的实现多线 ...
- Java多线程系列--“JUC线程池”06之 Callable和Future
概要 本章介绍线程池中的Callable和Future.Callable 和 Future 简介示例和源码分析(基于JDK1.7.0_40) 转载请注明出处:http://www.cnblogs.co ...
- Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例
概要 前面对"独占锁"和"共享锁"有了个大致的了解:本章,我们对CountDownLatch进行学习.和ReadWriteLock.ReadLock一样,Cou ...
- Java多线程系列--“JUC锁”06之 Condition条件
概要 前面对JUC包中的锁的原理进行了介绍,本章会JUC中对与锁经常配合使用的Condition进行介绍,内容包括:Condition介绍Condition函数列表Condition示例转载请注明出处 ...
- Java多线程系列--“JUC锁”05之 非公平锁
概要 前面两章分析了"公平锁的获取和释放机制",这一章开始对“非公平锁”的获取锁/释放锁的过程进行分析.内容包括:参考代码获取非公平锁(基于JDK1.7.0_40)释放非公平锁(基 ...
随机推荐
- js中函数的写法
js提供了灵活的函数写法,我们常见的函数写法和调用可能是: function ask(){ console.log(1); } ask(); 这样就完成了函数的定义和调用,司空见惯. 还有js里面的匿 ...
- Android软键盘弹出,覆盖h5页面输入框问题
之前我们在使用vue进行 h5 表单录入的过程中,遇到了Android软键盘弹出,覆盖 h5页面 输入框 问题,在此进行回顾并分享给大家: 系统:Android 条件:当输入框在可视区底部或者偏下的位 ...
- TestNG 相对路径与绝对路径getResourceAsStream
以下内容引自: http://blog.csdn.net/zmx729618/article/details/51144588 (注: 此url并非原出处,该文章也是转自他人.但博主未注明出处) Ja ...
- Linux.Centos6编译安装nginx
环境 系统环境:CentOS release 6.7 (Final) 需求 centos6.7编译安装nginx1.x 准备 安装依赖 yum install -y gcc gcc-c++ autoc ...
- Nodejs.调用Linux命令
当需要Node.js在后台帮忙运行Linux命令脚本时, 可以用上以下方法 上代码 var spawn = require('child_process').spawn; free = spawn(' ...
- Dubbo中服务消费者和服务提供者之间的请求和响应过程
服务提供者初始化完成之后,对外暴露Exporter.服务消费者初始化完成之后,得到的是Proxy代理,方法调用的时候就是调用代理. 服务消费者经过初始化之后,得到的是一个动态代理类,InvokerIn ...
- BZOJ_3191_[JLOI2013]卡牌游戏_概率DP
BZOJ_3191_[JLOI2013]卡牌游戏_概率DP Description N个人坐成一圈玩游戏.一开始我们把所有玩家按顺时针从1到N编号.首先第一回合是玩家1作为庄家.每个回合庄家都会随 ...
- BZOJ_[JSOI2010]Group 部落划分 Group_kruskal
BZOJ_[JSOI2010]Group 部落划分 Group_kruskal Description 聪聪研究发现,荒岛野人总是过着群居的生活,但是,并不是整个荒岛上的所有野人都属于同一个部落,野人 ...
- docker+mysql+zabix-server环境搭建
本次使用docker搭建zabbix的组合是mysql+docker+zabix-server 测试环境为:1.操作系统版本为:centos7.5 2.docker版本为:1.13.1 3 mysql ...
- linux学习笔记基础篇(一)
一.IP操作 1.临时修改IP ,执行命令 ifconfig 网卡名称 新ip :例如 ifconfg nescc 192.168.1.110 ,重启失效 2.重启网络 ,执行命令 syste ...