Java线程安全详解
并发与多线程
blog:https://devonmusa.github.io
1 常见概念
1.1 操作系统线程运行状态
NEW
RUNNABLE
RUNNING
BLOCKED
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 ...
随机推荐
- 即构SDK12月迭代:新增多项质量回调,互动白板、云录制SDK同步更新
即构SDK12月迭代来啦,本月LiveRoom/AudioRoom SDK新增了端到端延迟质量回调.房间会话ID信息,便于在音视频通话.直播场景中进行时延.通话质量的评测.同时还优化了硬件设备权限变更 ...
- 教师节专题:AI互动课来了,即构方案助推在线教育创新升级
打开热门综艺,乘风破浪的姐姐们告诉你"用瓜瓜龙英语给孩子启蒙":走出家门,电梯口.公交站的大幅广告跟你说"2-8岁上斑马". 如果说去年的AI互动课还是浮于媒体 ...
- SaaS软件工程师成长路径
背景 SaaS软件工程师的成长需要循序渐进,和SaaS业务一样有耐心.SaaS工程师需要在"业务"."技术"."管理"三个维度做好知识储备. ...
- Java 中怎样将 bytes 转换为 long 类型?
将bytes 转换为long类型: 第一种方式: String 接收 bytes 的构造器转成 String,再 Long.parseLong: 但此种情况需要注意:字节数组中的每个字节都必须是有效的 ...
- MAUI Blazor 显示本地图片的新思路
前言 好久没写文章了,水一篇 关于MAUI Blazor 显示本地图片这个问题,有大佬发过了. 就是 token 大佬的那篇 Blazor Hybrid (Blazor混合开发)更好的读取本地图片 主 ...
- 使用DBeaver连接数据库
下载网站:官网下载 参考链接:使用 DBeaver 连接 OceanBase
- React: 按钮点击时修改颜色
背景 当存在多个点击按钮时,需要提示用户点击的哪个按钮,所以要进行颜色的修改 import * as React from 'react'; import './style.css'; export ...
- Python3入门基础教程
引:此文是自己学习python过程中的笔记和总结,适合有语言基础的人快速了解python3和没基础的作为学习的大纲,了解学习的方向.知识点:笔记是从多本书和视频上学习后的整合版. (一)初识pytho ...
- 基于Word2Vec的诗词生成器
基于Word2Vec制作的诗词生成器 1.什么是Word2Vec? Word2vec 是 Word Embedding 方式之一,属于 NLP 领域.它是从大量文本预料中以无监督方式学习语义知识的模型 ...
- 微服务下使用maven做多环境配置
分享技术,用心生活 前言:很多项目在开发,提测,上线时都会提前手动改一些配置文件来适应对应环境,麻烦不说了,而且也容易出错:生产环境的配置也容易暴露.基于此,我们基于spring cloud alib ...