线程安全

ThreadLocal是什么?保证线程安全

和自甴很熟 提交于 2019-12-29 03:05:19
早在JDK 1.2的版本中就提供 Java .lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。   当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。   从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。   所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。 ThreadLocal的接口方法 ThreadLocal类接口很简单,只有4个方法,我们先来了解一下: void set(Object value)设置当前线程的线程局部变量的值。 public Object get()该方法返回当前线程所对应的线程局部变量。 public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。 protected Object initialValue

原子操作

天涯浪子 提交于 2019-12-29 02:35:14
   "原子操作(atomic operation)是不需要synchronized",这是Java多线程编程的老生常谈了。   所谓原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (cpu上下文切换)。   定义:一个操作是原子的(atomic),如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。视作整体式原子性的核心。   首先处理器会自动保证基本的内存操作的原子性。处理器保证从系统内存当中读取或者写入一个字节是原子的,意思是当一个处理器读取一个字节时,其他处理器不能访问这个字节的内存地址,也就是lock住这块内存,在我关于信号量的随笔中有提到《windows核心编程》,JVM同样也是。   我们以decl (递减指令)为例,这是一个典型的"读-改-写"过程,涉及两次内存访问。设想在不同CPU运行的两个进程都在递减某个计数值,可能发生的情况是:   ⒈ CPU A(CPU A上所运行的进程,以下同)从内存单元把当前计数值⑵装载进它的寄存器中;   ⒉ CPU B从内存单元把当前计数值⑵装载进它的寄存器中。   ⒊ CPU A在它的寄存器中将计数值递减为1;   ⒋ CPU

2019最新整理JAVA面试题附答案

笑着哭i 提交于 2019-12-28 15:37:08
本人免费整理了Java高级资料,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G,需要自己领取。 传送门: https://mp.weixin.qq.com/s/igMojff-bbmQ6irCGO3mqA 包含的模块: 本文分为十九个模块,分别是:Java 基础、容器、多线程、反射、对象拷贝、Java Web 、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、MyBatis、RabbitMQ、Kafka、Zookeeper、MySQL、Redis、JVM 如下图所示: 共包含 208 道面试题,本文的宗旨是为读者朋友们整理一份详实而又权威的面试清单,下面一起进入主题吧。 ==================================================== 一. Java 基础模块 1.JDK 和 JRE 有什么区别? JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。 JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。 具体来说 JDK 其实包含了

第二章-线程安全性

折月煮酒 提交于 2019-12-28 15:13:33
要编写线程安全的代码, 核心在于对状态访问操作进行管理 ,特别是对共享(shared)和可变(mutable)的状态访问。 修复多线程问题的三种方式: 不在线程之间共享该状态变量 将状态变量修改为不可变的变量 在访问状态变量时使用同步 一般来说,访问某个变量的代码越少,就越容易确保对变量的所有访问都实现正确同步,同时也更容易找出变量在哪些条件下被访问。 当设计线程安全类时,良好的面向对象技术、不可修改性、以及明晰的 不变性规范 都能起到一定的帮助作用。 一种正确的编程方法:首先使代码正确运行,然后再提高代码的速度。即使如此,最好也只是当性能测试结果和应用需求告诉你必须提高性能,以及测量结果表明这种优化在实际环境中确实能带来性能提升时,才进行优化。 2.1什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。 无状态对象一定是线程安全的。大多数Servlet都是无状态的,只有当Servlet在处理请求时需要保存一些信息,线程安全性才会成为一个问题。 2.2原子性 竞态条件(Race Condition):不恰当的执行时序而出现不正确的结果。 2.2.1竞态条件 最常见的竞态条件类型就是“先检查后执行”:

并发容器类Map

浪尽此生 提交于 2019-12-28 15:13:06
目录 目标 HashMap原理 ConcurrentHashMap ConcurrentSkipListMap 目标 HashMap ConcurrentHashMap 认识了解ConcurrentSkipListMap HashMap内部结构和原理 线程不安全:扩容时是非原子操作,会存在数据不完整,读的时候会存在问题,线程不安全。 从抽象角度,带着问题看源码,看最熟悉的方法,主要关注是什么而不是为什么这么做。 JDK1.7版本 存储数据结构:数据Entry<K,V>[] entry和链表结构,即数组加链表结构。 hash值:散列值,很难重复。 取模:计算数组下标。 hash碰撞:即使hash值不同,也可能下标相同。 扩容条件:是否达到指定的阈值,达到则扩容2倍。 JDK1.8版本 最大区别:当链表的长度大于8时,将链表转换成红黑树,提升查找的效率。 ConcurrentHashMap JDK1.7版本 线程安全:使用分段锁来保证线程安全。 存储数据方式:Segment<K,V> segment[],此数组的长度也是并发级别,确定之后不可改变,默认值16,一个特殊的hashTable,使用 分段锁 的技术,太多并发级别消耗性能, 扩容:segments数组加链表的结构,原理和hashMap类似。 JDK1.8版本 线程安全:通过CAS争抢链表头部,失败后

Java面试题13——线程安全之ReentrantLock和synchronized的面试题

妖精的绣舞 提交于 2019-12-28 02:53:14
上一篇,也就是第12篇的内容可以出好几道面试题,面试题如下 1.说说线程安全问题? 线程安全:线程安全指的是要控制多个线程对某个资源的有序访问或者修改,而这些线程之间没有冲突。 而线程安全问题指的是多个线程同时访问一个资源时产生的数据污染或者丢失的情况 产生线程安全问题的两个条件 多个线程操作共享的数据 操作共享数据的线程代码有多条 2. ReentrantLock 常见的方法有哪些? lock():用于获取锁 unlock():用于释放锁 trylock():尝试获取锁 trylock(long timeout ,TimeUnit unit);:在一定的时间内尝试去获取锁,而不是等待一段时间去获取 getHoldCount():计算lock方法的使用此时。 getQueueLength():返回正在排队等待获取此锁的线程数 isFair():是否为公平锁 3.ReentrantLock有哪些优势? 具备非阻塞方式获取锁,比如:tryLock() 可以中断所获取的锁,比如:使用interruptibly()方法,线程获得锁,会抛出异常并释放当前获得的锁。 可以在指定时间获取锁,比如:使用trylock(long timeout ,TimeUnit unit) 4.ReentrantLock怎么创建公平锁? new ReentrantLock()默认创建的为非公平锁 new

线程安全问题

淺唱寂寞╮ 提交于 2019-12-27 15:13:58
本篇主要讲解 线程安全问题,演示什么情况下会出现线程安全问题,以及介绍了 Java内存模型 、volatile关键字 、CAS 等 ,最后感谢吴恒同学的投稿! 一起来了解吧!!  1. 如何会发生线程安全  运行如下程序: /** * @program: * @description: 多线程操作的对象 * @author: * @create: **/ public class MyCount { private int myCount = 0 ; public int getMyCount() { return myCount; } public void setMyCount(int myCount) { this.myCount = myCount; } @Override public String toString() { return "MyCount{" + "myCount=" + myCount + '}'; } }  创建线程 public class CountThread1 extends Thread{ private MyCount myCount ; private static Object synch = new Object(); public CountThread1( MyCount myCount) { this.myCount =

OO第二次博客总结

断了今生、忘了曾经 提交于 2019-12-27 10:59:52
(1)从多线程的协同和步控制方面,分析总结自己三次作业来设计 策略及其变化。 多线程电梯: 由于是第一次接触多线程,我在还没有理解概念的情况下贸然上手,导致线程同步非常混乱。现在再来分析,发现思路其实还算清晰。InputHandler与调度器之间是生产者-消费者关系,中间应当有个线程安全类requestTray。调度器和各个电梯的存储队列间也是生产者-消费者关系。 IFTTT: 本次作业我认为我在多线程控制方面比电梯好很多,没有出现线程方面原因造成的bug。重要原因在于本次作业的多线程协同其实只出现在了文件操作方面,说白了是建立snapshot与测试线程修改文件之间的冲突。那么只需要在建立snapshot时上锁,同时将测试线程的所有方法上锁即可。 出租车: 我认为我这次的线程安全反而搞复杂了。InputHandler要读取电梯的状态,电梯自身要读取并修改自身的状态,请求监控器也要不断读取电梯状态。这样锁的切换之间造成的消耗很高,也容易出现bug。 (2) 基于度量来分析自己的程序结构 多线程电梯 序列图: 可以看到,本次作业的一些主要方法,比如主函数的run,电梯的update,判断同质请求的函数、进行捎带分配的函数,这些功能非常重要的函数的圈复杂度依然很高,显得很臃肿。一部分原因是因为这是电梯系列作业的第三次作业,所以自己一直在不断地在原用功能上加东西

Java单例模式探究

我们两清 提交于 2019-12-27 08:27:56
作为对象的创建模式 [GOF95] , 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。这个类称为单例类。由定义可以总结出单例模式的要点有三个:一是单例类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。 在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个 Printer Spooler ,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态,避免政出多头。 虽然从类图上看,单例模式是最简单的设计模式之一,但是真正正确地使用单例模式却不是那么简单的事。 首先看一个经典的单例实现。 public class Singleton { private static Singleton uniqueInstance = null ; private Singleton() { // Exists only to defeat instantiation. } public static Singleton getInstance() { if ( uniqueInstance ==

java复习--基础部分

拈花ヽ惹草 提交于 2019-12-27 07:14:12
1、数据类型   基本数据类型:8种;   引用数据类型:所有的类和对象,还有数组。 2、字符串String与可修改字符串StringBuffer、 StringBuilder的区别   字符串不是基本数据类型;   字符串是一个常量,是不可变的,每次修改都会生成一个新的字符串对象;   StringBuffer和StringBuilder是每次修改时不会产生新的对象,所以效率更高;   StringBuffer是线程安全的;   StringBuilder是非线程安全的。 3、数组特点    数据类型单一;   长度固定;   内存地址连续。 4、排序算法   想要将一列数按从小到大(或从大到小)的顺序排列-->   冒泡排序:每轮循环时,从头到尾依次比较相邻元素,不满足顺序就相互交换位置,直到所有元素都满足顺序,即可不再循环。   插入排序:(类似抓扑克牌) 将序列看成有序(第1个元素)和无序(第2个~最后一个)两组,每轮循环时,将无序序列中的首个元素插入到有序序列中的合适位置组成新的有序序列,直到无序序列的元素个数为0。   选择排序:把该列数看成无序序列,每轮循环时,从剩余的无序序列中找到最大(或最小)的元素放在第1位,直到剩余的无序序列的元素个数为0。   快速排序:(类型2分法) ,每次选择一个序列中的数作为基准数,将序列中的数按左小右大的规则放在基准数的合适位置