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 ...
随机推荐
- 详解nvim内建LSP体系与基于nvim-cmp的代码补全体系
2023年,nvim以及其生态已经发展的愈来愈完善了.nvim内置的LSP(以及具体的语言服务)加上众多插件,可以搭建出支持各种类型语法检查.代码补全.代码格式化等功能的IDE.网络上关于如何配置的文 ...
- Anchored Neighborhood Regression【阅读笔记】GR全局回归
论文信息 [Anchored Neighborhood Regression for Fast Example-Based uper Resolution]-TIMOFTER, 2013, IEEE ...
- 正确处理 CSV 文件的引号和逗号
CSV(Comma-Separated Values,逗号分割值),就是用纯文本的形式存储表格数据,最大的特点就是方便. 作为开发,我们经常面临导数据的问题,特别是后台系统,产品或者运营的同事常常会提 ...
- PTA 21级数据结构与算法实验5—树和二叉树
目录 7-1 还原二叉树 7-2 朋友圈 7-3 修理牧场 7-4 玩转二叉树 7-5 根据后序和中序遍历输出先序遍历 7-6 完全二叉树的层序遍历 7-7 列出叶结点 7-8 部落 7-9 建立与遍 ...
- Centos7 安装部署 Kubernetes(k8s) 高可用集群
目录 一.系统环境 二.前言 三.Kubernetes(k8s)高可用简介 四.配置机器基本环境 五.部署haproxy负载均衡器 六.部署etcd集群 七.部署Kubernetes(k8s) mas ...
- Java理论(一)
什么是java Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承.指针等概念,因 此Java语言具有功能强大和简单易用两个特征.Java语言作为静态面向 ...
- 虚拟化学习:vps和云计算的区别
1 vps 可以由虚拟机实现,但并不是虚拟机; 虚拟机是云计算的核心,但虚拟机也不等于云主机. 2 vps是面向单台服务器的虚拟化技术,服务器挂了,其上的vps也跟着挂了. 3 云计算是面向服务器集群 ...
- 如何通过cookie、session鉴权(nodejs/koa)
http是一种无状态的协议,每一个请求都是独立的,即使同一个页面向服务器发送多次请求,服务器也无法区分是不是同一用户,所以这个时候可以借助于cookie来做身份认证,当用户登录成功,服务器为浏览器设置 ...
- 论文解读(AAD)《Knowledge distillation for BERT unsupervised domain adaptation》
Note:[ wechat:Y466551 | 可加勿骚扰,付费咨询 ] 论文信息 论文标题:Knowledge distillation for BERT unsupervised domain a ...
- vue实现文本复制
一. 下载插件 npm install --save vue-clipboard2 二. main.js import VueClipBoard from 'vue-clipboard2' Vue.u ...