线程安全

Java并发--线程安全策略

喜夏-厌秋 提交于 2019-12-24 01:18:51
1 不可变对象 用不可变对象保证线程安全,是相当于不让线程并发,逃避了并发。 不可变对象就是指一个类的实例化对象不可变。比如String类的实例 主要方法有:   将类声明为final   将所有成员声明为 private   对变量不提供 set 方法,将所有可变成员声明为 final,只能赋值一次,通过构造器初始化所有成员,进行深度拷贝   在 get 方法中不直接返回对象本身,而是克隆对象,并返回对象的拷贝。 final关键字 一个类的private方法会隐式的指定为final方法 final还可以修饰方法的参数,表示该参数在方法中使用时不可以修改。 这里需要注意的是,final修饰的引用数据类型中其内部数据还是可以被修改的,比如一个final修饰的map,有数据<1,3>,可以被修改为<1,2>,所以并不是线程安全的。需要进行解决。 Collections.unmodifiableXXX 系列方法,可以解决上述问题 ,它可以将输入的引用对象变为不可变对象,内部也是不可变的 Guava:ImmutableXXX系列方法也可以达到相同的效果 2 线程封闭 把对象封装到一个线程中,只有这一个线程可以看到这个对象,那么这个对象就不用考虑线程安全问题。 下面用例子演示ThreadLocal的使用: 参考博客: 面试官再问你 ThreadLocal,你就这样“怼”回去!   

java

廉价感情. 提交于 2019-12-24 00:45:54
第十六章 Java final类不能继承、重写,final方法不能重写,final属性不能变 16.1 JVM 组成 JVM内存大致分为五个区域:方法区、虚拟机栈、本地方法栈、堆、程序计数器 **程序计数器:**记录的是正在执行的虚拟机字节码指令的地址,通过改变程序计数器,java程序才能按顺序、循环、跳转等流程执行各个方法。该区域是所有区域中唯一没有定义内存溢出错误的区域。 **虚拟机栈:**java为每个方法保存状态信息的区域,这里存放的是每个方法中的局部变量、方法出口、动态链接等,著名的栈溢出错误就是在这里发生。 **本地方法栈:**java可以执行非java函数,这些函数的状态信息就保存在这个区域,因此这个区域也有可能发生栈溢出。 **堆:**一块线程共享的存放对象实例和数组的内存区域,线程安全问题的根本原因,也是整个内存区域中最大的一块。 **方法区:**存储已被加载的类信息、常量、静态变量等,著名的常量池就位于这里。 类加载机制 当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过 加载、连接、初始化 3个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化。 **加载:**将类的class文件读入到内存,创建一个类对象的过程,加载的方法有三种: new的方式加载、调用类反射的方法加载

Volatitle 修饰i i++线程安全吗

烂漫一生 提交于 2019-12-23 19:00:44
** Volatitle 修饰i i++线程安全吗 ** 首先 <分析Volatitle的问题> 1》Volatitle并不能解决非原子性操作的多线程安全问题。 2》Volatitle解决的是多线程共享变量间的可见性问题 3》使用Volatitle会增加性能开销。 其次 <分析i++的线程安全问题> 1》如果i是局部变量,则是线程安全的。因为局部变量中的每个线程都有自己的栈,它们之间不会发生冲突。 2》如果i是全局变量,则不是线程安全的。因为在全局变量中每个进程中的多个线程都可以访问到该变量。 本质上讲,并不是因为i是全局变量而导致她的线程不安全性。而是因为i++这个操作本身是非原子的。原子有不可分特性。如果是原子的话,即便多个线程都能访问到,它也是线性安全的。 3》i++这个过程执行了多个操作,首先读取i,然后值+1,最后将+1后的值写回i中。 最后 Java提供了java.util.concurrent.atomic 包来提供线程安全的基本类型包装类,但是使用synchronized或者AtomicXX系列的包装类时也会增加性能开销 来源: CSDN 作者: ancientidiot 链接: https://blog.csdn.net/ancientidiot/article/details/103660774

并发基础知识 — 线程安全性

删除回忆录丶 提交于 2019-12-23 18:55:54
前段时间看完了《并发编程的艺术》,总感觉自己对于并发缺少一些整体的认识。今天借助《Java并发编程实践》,从一些基本概念开始,重新整理一下自己学过并发编程。从并发基础开始,深入进去,系统学习一下并发编程。   编写线程安全的代码,核心在于要对状态访问操作进行管理,特别是对共享的(Shared)和可变的(Mutable)状态的访问。对象的状态是指存储在状态变量(实例或静态域)中的数据。对象的状态还可能包括其他依赖对象的域。(Map.Entry)   一个对象是否需要时线程安全的,取决于该对象是否被多线程访问。这指的是程序中访问对象的方式,而不是对象要实现的功能。要使得对象是线程安全的,要采用同步机制来协同对对象可变状态的访问。Java常用的同步机制是 Synchronized ,还包括 volatile 类型的变量,显示锁以及原子变量。   线程安全的程序是否完全由线程安全的类构成?答案是否定的,完全由线程安全的类构成的程序并不一定是线程安全的,线程安全类中也可以包含非线程安全的类。只有当类中仅包含自己的状态时,线程安全类才有意义! 什么是线程安全性?   当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么这个类就是线程安全的。   正确性:某个类的行为与其规范相一致。

JUC 基础内容概述

半城伤御伤魂 提交于 2019-12-23 10:50:29
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> Concurrent Programming in Java 的作者 Doug Lea 编写了一个极其优秀的、免费的并发实用程序包,它包括并发应用程序的锁、互斥、队列、线程池、轻量级任务、有效的并发集合、原子的算术操作和其它基本构件。我们一般称这个包为 J.U.C。 1. JUC概况 以下是Java JUC包的主体结构: Atomic : AtomicInteger Locks : Lock, Condition, ReadWriteLock Collections : Queue, ConcurrentMap Executer : Future, Callable, Executor Tools : CountDownLatch, CyclicBarrier, Semaphore 2. 原子操作 多个线程执行一个操作时,其中任何一个线程要么完全执行完此操作,要么没有执行此操作的任何步骤,那么这个操作就是原子的。出现原因: synchronized的代价比较高。 以下以AtomicInteger为例: int addAndGet(int delta):以原子方式将给定值与当前值相加。 实际上就是等于线程安全版本的i =i+delta操作。 boolean compareAndSet(int expect,

String、StringBuffer以及StringBuilder的各自优缺点及区别

落爺英雄遲暮 提交于 2019-12-23 04:00:34
文章目录 String类 字符串缓冲区 String与StringBuffer的区别 为什么StringBuffer线程安全效率低,而StringBuilder线程不安全效率高 String类 String类代表字符串, 字符串是一个常量,值创建后不能改变,底层是一个字符数组 final的出现就是为了为了不想改变,而不想改变的理由有两点:设计(安全)或者效率。 final 修饰的类是不被能继承的,所以 final 修饰的类是不能被篡改的 String值为什么不能改变? String类值不可变指的是引用地址不能改变,但是引用地址指向的数组是可以改变的,引用地址不能改变的原因是被final修饰,final修饰的变量是不能改变的 String的构造方法:   1、new String()创建一个空的字符序列,称为空串   2、 String(byte[] bytes) /String(byte[] bytes, Charset charset)通过字节数组创建字符串对象,第二个参数为指定字符编码   3、String(char[] value) /String(char[] value, int offset, int count)将字符数组转化为字符串   4、 String(String original)创建一个字符串 如 new String(“123”); 什么是空串

并发之懒汉饿汉的单例模式线程安全问题

情到浓时终转凉″ 提交于 2019-12-22 21:41:21
饿汉模式: 本身线程安全,在类加载时就已经进行了实例化,无论之后用不用的到。 package com . cljtest . demo . thread ; public class HungerSingleton { public static HungerSingleton hungerSingleton = new HungerSingleton ( ) ; public static HungerSingleton getInstance ( ) { return hungerSingleton ; } //构造方法私有化 private HungerSingleton ( ) { } public static void main ( String [ ] args ) { //起10个线程进行结果测试 for ( int i = 0 ; i < 10 ; i ++ ) { new Thread ( ( ) - > { System . out . println ( HungerSingleton . getInstance ( ) ) ; } ) . start ( ) ; } } } 一定要注意无参构造方法的私有化问题,如果 设成了public,其他类里实例化时直接new一个无参的构造方法,会造成单例模式失去意义。 由于类加载时就已经实例化了

HashMap,HashTable,ConcurrentHashMap

限于喜欢 提交于 2019-12-22 19:03:02
HashMap,HashTable,ConcurrentHashMap 一.HashTable 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化 初始size为11,扩容:newsize = olesize*2+1 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length 二.HashMap 1.7版本以前底层数组+链表实现,1.8版本后为数组+链表/红黑树,可以存储null键和null值,线程不安全 初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂 扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入 插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容) 当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀 计算index方法:index = hash & (tab.length – 1) HashMap的初始值还要考虑加载因子: 哈希冲突:若干Key的哈希值按数组大小取模后,如果落在同一个数组下标上,将组成一条Entry链

oo第四单元总结

老子叫甜甜 提交于 2019-12-22 11:23:02
oo第四单元总结 1. 总结本单元两次作业的架构设计 第四单元的两次作业都是对UML模型文件进行解析,主要关注类图、顺序图和状态图模型层次的语义观察,UML模型间关系,模型图表达的内容及关系,模型图之间的关系和一致性检查。 1.1第一次作业 第一次作业的主要是完成对UML类图的查询。我的思路是对类和类中的属性和操作用HashMap保存,对于关联,继承和顶级父类,建立静态数组保存类和接口之间的关系。对于函数的调用仅是返回图中的值。这次作业主要是要理解UML解析文件,需要发现如何找到属性和操作对应的类以及继承和关联关系是如何实现的。 第一次作业中我的代码有两个bug,一是在统计类实现的全部接口时,我用HashMap存接口间的继承关系,在多继承的情况下,会导致有的继承关系没有被记录。二是在类是否违背信息隐藏原则时,应该传出属性所在类的类名,我都传出了传进来的那个类名。对多继承没有使用恰当的保存方式以及对题意理解不充分。 1.2第二次作业 第二次作业的主要内容是对状态图,顺序图的查询和对类图的三个检查。在这次作业中我复用了第一次作业的代码,但我第一次作业的代码不能处理循环继承的问题,所以在MyUmlGeneralInteraction初始化时会出现死循环,需要更改初始化顺序。 2. 总结自己在四个单元中架构设计及OO方法理解的演进 第一单元 第一单元我基本没有架构设计

Java线程安全和锁Synchronized

倖福魔咒の 提交于 2019-12-21 23:49:14
进程与线程的概念 (1)在传统的操作系统中,程序并不能独立运行,作为资源分配和独立运行的基本单位都是进程。 在未配置 OS 的系统中,程序的执行方式是顺序执行,即必须在一个程序执行完后,才允许另一个程序执行;在多道程序环境下,则允许多个程序并发执行。程序的这两种执行方式间有着显著的不同。也正是程序并发执行时的这种特征,才导致了在操作系统中引入进程的概念。 自从在 20 世纪 60 年代人们提出了进程的概念后,在 OS 中一直都是以进程作为能拥有资源和独立运行的基本单位的。直到 20 世纪 80 年代中期,人们又提出了比进程更小的能独立运行的基本单位——线程(Thread),试图用它来提高系统内程序并发执行的程度,从而可进一步提高系统的吞吐量。特别是在进入 20 世纪 90 年代后,多处理机系统得到迅速发展,线程能比进程更好地提高程序的并行执行程度,充分地发挥多处理机的优越性,因而在近几年所推出的多处理机 OS 中也都引入了线程,以改善 OS 的性能。 通过上述的大致了解,基本知道线程和进程是干什么的了,那么我们下边给进程和线程总结一下概念: (3)进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器