Java线程安全详解
并发与多线程
blog:https://devonmusa.github.io
1 常见概念
1.1 操作系统线程运行状态
NEWRUNNABLERUNNINGBLOCKED
1.2 Java虚拟机线程实际运行状态
public enum State {NEW尚未开始的线程处于此状态,即刚创建线程对象,未调用start()方法RUNNABLE新建线程调用start()方法后,在Java虚拟机中执行的线程处于此状态,但它可能正在等待来自操作系统的其他资源(如处理器)。BLOCKED处于阻塞状态的线程正在等待监视器锁定进入同步块或同步方法,或者重入锁进入同步块或同步方法后调用Object.wait()方法WAITING由于调用以下方法之一,线程处于等待状态Object.wait(); 无超时Thread.join(); 无超时LockSupport.park();例如,a.在对象上调用Object.wait()的线程正在等待另一个线程调用Object.notify()或Object.notifyAll()在该对象。b.一个名为Thread.join(的线程正在等待指定的线程终止。TIMED_WAITING由于调用一个线程,线程处于定时等待状态,使线程处于定时等待状态可调用方法如下:Thread.sleep(long);Object.wait(long)Thread.join(long);LockSupport.parkNanos(long);LockSupport.parkUntil(long);TERMINATED终止线程的线程状态。 即线程已完成当前任务的执行。}
1.3 重入锁
概念:
一个线程试图获得它自己持有的锁,那么这个请求就会成功
实现原理:
1.为每个锁绑定所有者和计数值,每重入一次该计数值自增1,而当线程每退出一次计数值递减,直到计数值为零才释放锁。
常用实现
1.4 sychroinzed
概述:
在JDK1.6之前被称为重量级锁,然后在JDK1.6中为了减少获得和释放锁所带来的性能消耗引入了偏量锁和轻量级锁
实现原理:
JVM规范中都是给予进入和对出Monitor对象来实现方法同步和代码块同步的,但两者的实现细节不同:代码块同步是使用monitorenter和monitorexit指令实现的
使用形式:
普通方法+syncronized
锁是当前实例对象
- public class InstanceObjectSyncronizedTest{
- private Integer num = 5;
- public syncronized Integer getNum(){
- num++
- return num;
- }
- }
静态方法+syncronized
锁是当前类的Class对象
- public class InstanceObjectSyncronizedTest{
- private static Integer num = 5;
- public static synchronized void IntegergetNum() {
- ++num;
- }
- }
方法块+syncronized(Object.Class)
锁是当前类的Class对象
方法块+syncronized
锁是syncronized括号里配置的对象
1.5 lock接口及实现
1.6 volatile
volatile是轻量级的sncronized的实现,只修饰变量,多线程访问不会发生阻塞,但volatile不具备原子性,功能使变量在多线程间可见。
使用方式:
1)确保自身状态的可见性2)确保他们所引用的对象的可见性
应用场景:
1)标记循环控制状态,检查状态来判断是否退出循环2)单线程写,多线程读
1.7 线程池
拒绝策略
重试策略
public static class CallerRunsPolicy implements RejectedExecutionHandler {/*** Creates a {@code CallerRunsPolicy}.*/public CallerRunsPolicy() { }/*** Executes task r in the caller's thread, unless the executor* has been shut down, in which case the task is discarded.** @param r the runnable task requested to be executed* @param e the executor attempting to execute this task*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {r.run();}}}
异常抛出策略
/*** A handler for rejected tasks that throws a* {@code RejectedExecutionException}.*/public static class AbortPolicy implements RejectedExecutionHandler {/*** Creates an {@code AbortPolicy}.*/public AbortPolicy() { }/*** Always throws RejectedExecutionException.** @param r the runnable task requested to be executed* @param e the executor attempting to execute this task* @throws RejectedExecutionException always*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {throw new RejectedExecutionException("Task " + r.toString() +" rejected from " +e.toString());}
} 旧任务丢弃策略
/*** A handler for rejected tasks that silently discards the* rejected task.*/public static class DiscardPolicy implements RejectedExecutionHandler {/*** Creates a {@code DiscardPolicy}.*/public DiscardPolicy() { }/*** Does nothing, which has the effect of discarding task r.** @param r the runnable task requested to be executed* @param e the executor attempting to execute this task*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {}}
当前任务丢弃策略
/*** A handler for rejected tasks that discards the oldest unhandled* request and then retries {@code execute}, unless the executor* is shut down, in which case the task is discarded.*/public static class DiscardOldestPolicy implements RejectedExecutionHandler {/*** Creates a {@code DiscardOldestPolicy} for the given executor.*/public DiscardOldestPolicy() { }/*** Obtains and ignores the next task that the executor* would otherwise execute, if one is immediately available,* and then retries execution of task r, unless the executor* is shut down, in which case task r is instead discarded.** @param r the runnable task requested to be executed* @param e the executor attempting to execute this task*/public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {if (!e.isShutdown()) {e.getQueue().poll();e.execute(r);}}}
2 线程安全模型
2.1 COW(copy on write)
来源于linux的fork命令,java中的实现类CopyOnWriteArrayList 读操作无需加锁,可能会出现脏数据,遍历时被其他线程修改不会出现并发修改异常(CurrentModificationException)
2.2 CAS(compare and swap)
即比较与替换被封装为原子操做,ConcurrentHashMap和HashMap都采用数组+链表+红黑数的结构存储数据
2.3 RWS(read write separate)
即读写分离,LinkedBlockingQueue,用两个可重入锁枷锁head和last
3 应用场景
3.1 多读少写
3.2 多写少读
4 线程安全的类的设计
4.1 判断类是否是线程安全的
- 找出构成对象状态的所有变量
- 找出约束状态变量的不变性条件
- 建立对象状态的并发访问管理策略
原文链接:https://github.com/Devonmusa/demos-parent/blob/master/language/java/doc/并发和多线程.md
Java线程安全详解的更多相关文章
- Java线程池详解(二)
一.前言 在总结了线程池的一些原理及实现细节之后,产出了一篇文章:Java线程池详解(一),后面的(一)是在本文出现之后加上的,而本文就成了(二).因为在写完第一篇关于java线程池的文章之后,越发觉 ...
- java 线程状态 详解
线程被创建后,有一个生命周期,下图是线程的生命周期详解. java api java.lang.Thread.State 这个枚举中给出了六种线程状态,分别是: 线程状态 导致状态发生条件 NEW(新 ...
- Java线程池详解
一.线程池初探 所谓线程池,就是将多个线程放在一个池子里面(所谓池化技术),然后需要线程的时候不是创建一个线程,而是从线程池里面获取一个可用的线程,然后执行我们的任务.线程池的关键在于它为我们管理了多 ...
- 【java线程系列】java线程系列之java线程池详解
一线程池的概念及为何需要线程池: 我们知道当我们自己创建一个线程时如果该线程执行完任务后就进入死亡状态,这样如果我们需要在次使用一个线程时得重新创建一个线程,但是线程的创建是要付出一定的代价的,如果在 ...
- Java线程池详解,看这篇就够了!
构造一个线程池为什么需要几个参数?如果避免线程池出现OOM?Runnable和Callable的区别是什么?本文将对这些问题一一解答,同时还将给出使用线程池的常见场景和代码片段. 基础知识 Execu ...
- Java线程池 详解(图解)
来源:www.jianshu.com/p/098819be088c 拓展: 手动创建 new ThreadPoolExecutor 的使用: https://segmentfault.com/a/11 ...
- Java 线程池详解
Executors创建线程池 Java中创建线程池很简单,只需要调用Executors中相应的便捷方法即可,比如Executors.newFixedThreadPool(int nThreads),但 ...
- Java线程池详解(一)
一.线程池初探 所谓线程池,就是将多个线程放在一个池子里面(所谓池化技术),然后需要线程的时候不是创建一个线程,而是从线程池里面获取一个可用的线程,然后执行我们的任务.线程池的关键在于它为我们管理了多 ...
- Java线程学习详解
线程基础 1. 线程的生命周期 1.1 新建状态: 使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态.它保持这个状态直到程序 start() 这个线程. 1 ...
- Java线程常用方法详解
线程的常用方法 1.start() : 线程调用该方法将启动线程,使之从新建状态进入就绪队列排队,一旦轮到它来享用CPU资源时,就可以脱离创建它的线程独立开始自己的生命周期了. 2.run(): Th ...
随机推荐
- Jenkins自动化测试构建完成 发送钉钉消息
背景 有时自动化测试完成后,我们可以通过构建完成后给钉钉群发消息,这样就能及时通知到所有人员了. 接入流程 1:建立钉钉机器人,可以通过群助手,添加机器人,增加WebHook自定义接入,然后添加完成会 ...
- 简单的python3脚本:从日志中提取信息
命名:log_extractor.py or download_stats_extractor.py # coding:utf-8 #!/usr/bin/python3 def filter_line ...
- Mysql报错:Specified key was too long; max key length is 767 bytes
1.show variables like 'innodb_large_prefix'; show variables like 'innodb_file_format'; 修改为如下配置: set ...
- 王道oj/problem12(动态申请内存存储数组)
网址:http://oj.lgwenda.com/problem/12 思路:用输入的整型创建对应数组,用scanf消除换行键: 用gets()输入语句并输出,再释放. 代码: #define _CR ...
- [数据分析与可视化] Python绘制数据地图4-MovingPandas入门指北
MovingPandas是一个基于Python和GeoPandas的开源地理时空数据处理库,用于处理移动物体的轨迹数据.它提供了一组强大的工具,可以轻松地加载.分析和可视化移动物体的轨迹.通过使用Mo ...
- 端路由原理及react-router的常用组件
在react中,通常都是使用单页面应用(SPA),即整个页面只有一个html,然后通过不同的url地址进行组件的匹配和切换. 我们看到的url地址可能会有两种形式,一种是 localhost:3000 ...
- 自定义javascript中call、bind、apply方法
call.bind.apply都是Function原型上的方法,用于改变this的指向 自定义函数 js中的call.bind.apply是用c++代码实现的,我们这里使用js代码做一个模式,没有把所 ...
- 《CUDA编程:基础与实践》读书笔记(1):CUDA编程基础
1. GPU简介 GPU与CPU的主要区别在于: CPU拥有少数几个快速的计算核心,而GPU拥有成百上千个不那么快速的计算核心. CPU中有更多的晶体管用于数据缓存和流程控制,而GPU中有更多的晶体管 ...
- C#程序变量统一管理例子 - 开源研究系列文章
今天讲讲关于C#应用程序中使用到的变量的统一管理的代码例子. 我们知道,在C#里使用变量,除了private私有变量外,程序中使用到的公共变量就需要进行统一的存放和管理.这里笔者使用到的公共变量管理库 ...
- [超详细]SpringBoot整合WebSocket
1. 什么是WebSocket? WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,它允许在浏览器和服务器之间进行实时的.双向的通信.相对于传统的基于请求和响应的 HTTP 协议, ...