线程安全

如何线程安全的使用HashMap

夙愿已清 提交于 2019-11-30 06:04:47
为什么HashMap是线程不安全的 总说HashMap是线程不安全的,不安全的,不安全的,那么到底为什么它是线程不安全的呢?要回答这个问题就要先来简单了解一下HashMap源码中的使用的存储结构(这里引用的是Java 8的源码,与7是不一样的)和它的扩容机制。 HashMap的内部存储结构 下面是HashMap使用的存储结构: transient Node<K,V>[] table; static class Node<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Node<K,V> next; } 可以看到HashMap内部存储使用了一个Node数组(默认大小是16),而Node类包含一个类型为Node的next的变量,也就是相当于一个链表,所有hash值相同(即产生了冲突)的key会存储到同一个链表里,大概就是下面图的样子(顺便推荐个在线画图的网站Creately)。 需要注意的是,在Java 8中如果hash值相同的key数量大于指定值(默认是8)时使用平衡树来代替链表,这会将get()方法的性能从O(n)提高到O(logn)。具体的可以看我的另一篇博客Java 8中HashMap和LinkedHashMap如何解决冲突。http://yemengying.com/2016/02

设计线程安全的类

◇◆丶佛笑我妖孽 提交于 2019-11-30 03:04:47
设计线程安全的类 有时调用是非线程安全的,此时必须把有安全隐患的方法用同步代码块包括起来,所谓线程安全的类就是将同步机制封装在类内部,这样方法调用时就不存在线程安全问题了。 要设计线程安全的类首先寻找可能暴露在外的字段,包括private但是内部方法修改的。然后将这些字段用同步手段保护起来,不仅仅要在修改时保护,在访问时也要保护,以保证时刻读到正确的值。 在保护时注意维护不变性条件,例如当一个类存在缓存机制时,会有版本号和缓存值一一对应的情况,取值时根据版本号取缓存,如果不一致再计算新值并更新版本号,但是这种更新不是完全同步的,多线程下会有两者不一致的情况,此时要把涉及更新的步骤同步起来保证一致。 volatile volatile并不保证原子性,只保证可见性。所以使用时必须保证对该字段的每次更新都不依赖从前的值,且不构成不变性条件,此时这种保护是足够的。 final 以不可变的方式保证线程安全(对于引用类型必须把它内部的字段全部设置成final才能绝对保证线程安全) threadlocal 它解决了线程共享的问题,new一个threadlocal方法set一个值再get就能得到,它相当于一个线程私有的容器。 避免this引用溢出 在一个类的构造方法中将this赋值给其他字段,这样其他线程可能获取该字段的值来获取this,一旦获取到this其中的final字段也就变成不安全的

集合框架整理

梦想的初衷 提交于 2019-11-30 02:59:38
List接口 List @since JDK1.0,List是一个有序的、可以重复、可以为null 的集合(有时候我们也叫它“序列”)。 ArrayList @since 1.2,是基于数组实现的,是一个动态数组,查询效率高,增删效率低,线程不安全。 LinkedList @since 1.2,底层用双向链表实现的存储。查询效率低,增删效率高,线程不安全。 Vector @since JDK1.0,和ArrayList类的用法几乎一模一样。线程安全,效率低。(被ArrayList代替) Map 接口 Map @since 1.2,以“ 键(key)-值(value) 对 ”的方式进行存储。“(key) 键 ”不能重复( 去重 )。 HashMap @since 1.2,采用哈希算法实现,是Map接口最常用的实现类。允许key-value为空,输出 自动 排列,“(key)键”不能重复,如果发生重复,新键值对会替换旧的键值对( 去重 )。HashMap线程不安全, 查找 、 删除 、 修改 效率 高 。 LinkedHashMap @since 1.4,是 HashMap 集合的子集合,底层采用 哈希表+链表 结构。允许key-value为空,输出 有序 排列,key不能重复,,如果发生重复,新键值对会替换旧的键值对( 去重 )。 Hashtable @since JDK1.0

17个经典的Spring面试问答

故事扮演 提交于 2019-11-30 02:46:57
Q1、什么是Spring Framework? Spring是Java企业版应用程序开发中使用最广泛的框架.Spring的核心功能可用于开发任何Java应用程序。 我们可以使用它的扩展来在Java EE平台上构建各种Web应用程序,或者我们可以在简单的独立应用程序中使用它的依赖注入。 Q2、使用Spring有什么好处? Spring旨在简化Java EE开发。以下是使用它的优点: 轻量级: 在开发中使用框架开销很小 控制反转(IoC): Spring容器负责处理各种对象的依赖关系,而不是创建或查找依赖对象 面向切面编程(AOP): Spring支持AOP将业务逻辑与系统服务分开 IoC容器: 它管理Spring Bean生命周期和项目特定配置 MVC框架: 用于创建Web应用程序或RESTful Web服务,能够返回XML / JSON响应 事务管理: 通过使用Java注释或Spring Bean XML配置文件减少JDBC操作 异常处理: Spring提供了一个方便的API,用于将特定于技术的异常转换为未经检查的异常 Q3、你知道哪些Spring子项目?简要描述一下。 Core - 提供框架基本部分的关键模块,如IoC或DI JDBC - 此模块启用JDBC抽象层,无需对特定供应商数据库执行JDBC编码 ORM集成 - 为流行的对象关系映射API提供集成层,例如JPA

String 、StringBuffer、StringBuilder

独自空忆成欢 提交于 2019-11-30 00:44:16
简单从以下四方面来说其实很简单: 线程安全性: 线程安全 String 、StringBuffer 非线程安全 StringBuilder 执行效率: StringBuilder > StringBuffer > String 存储空间: String 的值是不可变的,每次对String的操作都会生成新的String对象, 效率低耗费大量内存空间,从而引起GC(垃圾回收机制)。 StringBuffer和StringBuilder都是可变。 使用场景: 1如果要操作少量的数据用 String 2.单线程操作字符串缓冲区 下操作大量数据 = StringBuilder 3.多线程操作字符串缓冲区 下操作大量数据 = StringBuffer 来源: https://blog.csdn.net/mateng52/article/details/100982746

java并发学习第三章--线程安全问题

一世执手 提交于 2019-11-29 22:02:55
   线程的安全问题一直是我们在开发过程中重要关注的地方,出现线程安全问题的必须满足两个条件:存在着两个或者两个以上的线程;多个线程共享了着一个资源, 而且操作资源的代码有多句。接下来我们来根据JDK自带的方法来解决线程带来的问题。 一、同步代码块synchronized    我们来看一个实例,创建两个线程,每个线程就对计算器i进行减1操作,当i等于0时停止线程 public class Main implements Runnable { int i = 10; @Override public void run() { while (i > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("这是线程" + Thread.currentThread().getName() + "当前的值是:" + i--); } } public static void main(String[] args) { Main main = new Main(); Thread t1 = new Thread(main); Thread t2 = new Thread(main); t1.start(); t2.start(); }

Java容器源码分析-高并发处理Map-ConcurrentHashMap和HashTable

徘徊边缘 提交于 2019-11-29 19:35:14
上一章节,分析了常用的Map集合,随着互联网多线程并发的场景越来越多,原始的数据结构已经无法满足真是的场景需求,所以这种线程安全的集合就显得非常重要。这里介绍一下ConcurrentHashMap和HashTable两种线程安全的数据集合。 那么在分析之前我们首先提出一个问题ConcurrentHashMap和HashTable在做线程安全时的区别? 这里介绍一个比较全面分析源码的帖子:http://blog.csdn.net/zhangerqing/article/details/8193118 总结: 1、HashTable与HashMap采用的数据结构一致。只不过HashTable是线程安全的,在主要的操作方法上都实现了synchronized同步 2、HashTable不允许null的key值存储,如果存储后会报NullPointerException异常 3、ConcurrentHashMap是线程安全的HashMap 4、HashTable和ConcurrentHashMap的区别在于实现锁的机制不一样,ConcurrentHashMap锁的是哈希表中的key,而HashTable锁的是整个哈希表,如下图(侵删) 来源: oschina 链接: https://my.oschina.net/u/3196846/blog/1553508

JAVA不可变类(immutable)机制与String的不可变性

主宰稳场 提交于 2019-11-29 18:58:15
一、不可变类简介 不可变类 :所谓的不可变类是指这个类的实例一旦创建完成后,就不能改变其成员变量值。如JDK内部自带的很多不可变类:Interger、Long和String等。 可变类 :相对于不可变类,可变类创建实例后可以改变其成员变量值,开发中创建的大部分类都属于可变类。 二、不可变类的优点 说完可变类和不可变类的区别,我们需要进一步了解为什么要有不可变类?这样的特性对JAVA来说带来怎样的好处? 线程安全 不可变对象是线程安全的,在线程之间可以相互共享,不需要利用特殊机制来保证同步问题,因为对象的值无法改变。可以降低并发错误的可能性,因为不需要用一些锁机制等保证内存一致性问题也减少了同步开销。 易于构造、使用和测试 ... 三、不可变类的设计方法 对于设计不可变类,个人总结出以下原则: 1. 类添加final修饰符,保证类不被继承 。 如果类可以被继承会破坏类的不可变性机制,只要继承类覆盖父类的方法并且继承类可以改变成员变量值,那么一旦子类以父类的形式出现时,不能保证当前类是否可变。 2. 保证所有成员变量必须私有,并且加上final修饰 通过这种方式保证成员变量不可改变。但只做到这一步还不够,因为如果是对象成员变量有可能再外部改变其值。所以第4点弥补这个不足。 3. 不提供改变成员变量的方法,包括setter 避免通过其他接口改变成员变量的值,破坏不可变特性。 4

线程安全 -同步锁机制

北城余情 提交于 2019-11-29 17:34:52
package cn.learn.thread.ThreadSafe; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /* 实现Runnable必须重写run()方法 安全问题解决方案:synchronized同步机制 -在共享数据操作的位置进行设置run() 过程: 3个线程一起抢占cpu的执行权,抢占成功,该线程执行run()方法,遇到synchronized代码块, 这时该线程会检查是否存在锁对象 有:就会获取到锁对象进入同步中执行 没有:(被前面的线程抢占)就会进入阻塞状态,会一直等待上一线程结束归还锁对象,获取到锁对象进入同步执行 总结:同步中的线程,没有执行完毕就不会释放锁对象,同步外的线程,没有锁对象就进不去内部 同步锁锁的是线程,锁的不是线程抢占cpu执行权 问题:频繁的判断锁,获取锁访问锁,程序效率降低 */ public class RunnableImpl implements Runnable { //第二种方法 //定义一个多线程共享资源 private static int tickets=100; //第一种方法 //创建锁对象,同步锁/对象锁/对象监视器 Object obj = new Object(); /

双重检查锁为什么要使用volatile字段?

此生再无相见时 提交于 2019-11-29 15:13:34
双重锁的由来 单例模式中,有一个DCL(双重锁)的实现方式。在Java程序中,有时候可能需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才开始初始化。 下面是非线程安全的延迟初始化对象的实例代码。 /** * @author xiaoshu */ public class Instance { } /** * 非线程安全的延迟初始化对象 * * @author xiaoshu */ public class UnsafeLazyInitialization { private static Instance instance; public static Instance getInstance() { if (null == instance) { instance = new Instance(); } return instance; } } 在UnsafeLazyInitialization类中,假设A线程执行代码1的同时,B线程执行代码2。此时,线程A可能会看到instance引用对象还没有完成初始化。 对于UnsafeLazyInitialization类,我们可以对 getInstance() 方法做同步处理来实现线程安全的延迟初始化。示例代码如下。 /** * 安全的延迟初始化 * * @author xiaoshu */ public class