-
2020-06-11 23:18:44
一、什么是线程不安全和线程安全
- 定义:线程不安全:多线程并发执行某个代码时,产生了逻辑上的错误,结果和预期值不相同
- 线程安全是指多线程执行时没有产生逻辑错误,结果和预期值相同
二、线程不安全产生的原因
- 线程是抢占执行的。
- 有的操作不是原子的。当 cpu 执行一个线程过程时,调度器可能调走CPU,去执行另一个线程,此线程的操作可能还没有结束;(通过锁来解决)
- 多个线程尝试修改同一个变量(一对一修改、多对一读取、多对不同变量修改,是安全的)
- 内存可变性
- 指令重排序:java的编译器在编译代码时,会针对指令进行优化,调整指令的先后顺序,保证原有逻辑不变的情况下,来提高程序的运行效率。
三、怎样解决线程不安全
- 加锁
锁的特点:互斥的,同一时刻只有一个线程可以 获取到锁,其他线程如果尝试获取锁,就会发生阻塞等待,等到刚那个线程 释放锁 ,此时剩下的线程再重新竞争锁
基本操作:加锁,解锁(释放锁)
- 使用锁 —— 关键字 synchronized
- 加到普通方法前:表示锁 this;
- 加到静态方法前:表示锁当前类的类对象(类对象就是 JVM运行时,将 .class 文件加载到内存中获取到的(类加载));对象名 . getClass() --》 获取类对象
- 加到某个代码之前,显示指定给某个对象加锁;
如果两个对象有两把锁,各自锁各自的,就不会涉及冲突 / 互斥
但是,一旦锁里边的内容出现问题,就可能出现死锁,其他程序就只能一直等待,此时就得重启
四、相关代码
import java.util.Scanner; public class ThreadDemo11 { public static void main(String[] args) { Object locker1 = new Object(); Object locker2 = new Object(); Thread t1 = new Thread() { @Override public void run() { synchronized (locker1.getClass()) { System.out.println("请输入一个整数:"); Scanner sc = new Scanner(System.in); int n = sc.nextInt(); System.out.println("n = " + n); } } }; t1.start(); Thread t2 = new Thread() { @Override public void run() { synchronized (locker2.getClass()) { System.out.println("t2 获取到锁了"); } } }; t2.start(); } }
更多相关内容 -
java解决线程不安全问题的方法
2017-07-10 21:56:18当多线程并发访问同一个资源时,容易出现不安全问题 有时候我们分析打印结果,发现没有问题,但是并不是真的没问题,可能我们经验不够,没有发现问题。为了放大问题是用Thread中的sleep()方法 Thread.sleep(100);...线程不安全问题
当多线程并发访问同一个资源时,容易出现不安全问题 有时候我们分析打印结果,发现没有问题,但是并不是真的没问题,可能我们经验不够,没有发现问题。为了放大问题是用Thread中的sleep()方法
Thread.sleep(100); //是当前线程睡眠100毫秒,让其他线程去抢资源,经常用来模拟网络延迟。放大线程中的问题。
解决方法:
(1)同步代码块
(2)同步方法
(3)锁机制(Lock)
1.同步代码块
synchronized( 同步锁){ //同步锁:同步监听对象/ 同步监听器 /互斥锁
需要同步操作的代码
}
对象的同步锁只是一个概念,可以想象在对象上标记 一个锁
java程序运行可以使用任何对象作为同步监听对象,但是一般我们将当前i并发访问的共同资源(多个线程同步共享的资源对象)作为同步监听对象。
注意:在任何时候,只允许一个线程拥有同步锁,谁拿到锁就进入代码块,其他线程只能在外面等
2.同步方法
使用synchronized修饰的方法叫做同步方法,保证该线程执行该方法的时候,其他线程只能等着。
同步锁:1.非static方法,同步锁是this。2.static方法,使用当前方法所在类的字节码对象(Apple.class)是同步锁
注意:
synchronized不能修饰run方法,修饰之后,一个线程就执行了所有的功能,线程出现串行,相当于单线程。
解决方法:将需要同步的 代码定义在一个新的方法中,并且该方法用synchronized修饰,再在run方法中调用该新的方法即可
synchronized提高安全性,但是性能降低了,使用时尽量减小它的作用域。eg:stringBuffer和StringBuilder的区别,stringBuffer的方法全都加了synchronized修饰。
类似的还有:ArrayList和vector HashMap和Hashtable 一般选择使用性能比较高的。
**单例模式-懒加载同步
一般懒汉式
优化懒汉式
饿汉式结论:单例模式使用 饿汉式;简单粗暴。
3.锁机制(Lock)
Lock是一个接口,实现提供更广泛的锁定操作可以比使用synchronized获得方法和报表。
锁是一种通过多个线程控制对共享资源的访问的工具。通常,一个锁提供对共享资源的独占访问:在一个时间只有一个线程可以获得锁和所有访问共享资源,需要先获得锁。
谢谢阅读! -
简述:如何解决HashMap线程不安全的问题?
2021-11-14 07:55:37怎么解决HashMap线程不安全的问题? 1.使用HashTable替代HashMap HashTable的put操作,有synchronized关键字修饰。 2.使用Map map = Collections.synchronizedMap(new HashMap()); 这个方法实际上返回了一个...jdk1.8中HashMap为什么线程不安全?
会出现数据覆盖。
JDK1.7和JDK1.8中HashMap为什么是线程不安全的?怎么解决HashMap线程不安全的问题?
1.使用HashTable替代HashMap
HashTable的put操作,有
synchronized
关键字修饰。2.使用
Map map = Collections.synchronizedMap(new HashMap());
这个方法实际上返回了一个
SynchronizedMap
。
SynchronizedMap 实现线程安全的方法也是比较简单的,所有方法都是先对锁对象 mutex 上锁,然后再直接调用 Map 类型成员变量 m 的相关方法。这样一来,线程在执行方法时,只有先获得了 mutex 的锁才能对 m 进行操作。因此,跟 Hashtable 一样,在同一个时间点,只能有一个线程对 SynchronizedMap 对象进行操作,虽然保证了线程安全,却导致了性能低下。这么看来,连 Hashtable 都被弃用了,那性能同样低下的 SynchronizedMap 还有什么存在的必要呢?别忘了,后者的构造方法需要传入一个 Map 类型的参数,也就是说它可以将非线程安全的 Map 转化为线程安全的 Map。
上面这段话出自: Hashtable,SynchronizedMap和ConcurrentHashMap线程安全对比
看看这个类的源码:public static <K,V> Map<K,V> synchronizedMap(Map<K,V> m) { return new SynchronizedMap<>(m); } private static class SynchronizedMap<K,V> implements Map<K,V>, Serializable { private static final long serialVersionUID = 1978198479659022715L; private final Map<K,V> m; // Backing Map final Object mutex; // Object on which to synchronize SynchronizedMap(Map<K,V> m) { this.m = Objects.requireNonNull(m); mutex = this; } ... } //SynchronizedMap的put方法,实际调用的还是HashMap自己的put方法 public V put(K key, V value) { synchronized (mutex) {return m.put(key, value);} }
3.使用ConcurrentHashMap
jdk1.8中ConcurrentHashMap通过 CAS+synchronized实现线程安全。
Java并发——ConcurrentHashMap(JDK 1.8)
-
为什么HashMap线程不安全?以及实现HashMap线程安全的解决方案
2021-09-02 23:41:45一、为什么HashMap线程不安全? 1、JDK1.7 扩容引发的死循环和数据丢失 (1).当前jdk1.7版本的HashMap线程不安全主要是发生在扩容函数中,其中调用了HshMap的transfer()方法 //jdk 1.7的transfer方法,HashMap的扩...一、为什么HashMap线程不安全?
1、JDK1.7 扩容引发的死循环和数据丢失
(1).当前jdk1.7版本的HashMap线程不安全主要是发生在扩容函数中,其中调用了HshMap的transfer()方法
//jdk 1.7的transfer方法,HashMap的扩容操作 void transfer(Entry[] newTable, boolean rehash) { int newCapacity = newTable.length; for (Entry<K,V> e : table) { while(null != e) { Entry<K,V> next = e.next; if (rehash) { e.hash = null == e.key ? 0 : hash(e.key); } int i = indexFor(e.hash, newCapacity); e.next = newTable[i]; newTable[i] = e; e = next; } } }
在执行数组扩容操作时,数据会重新定位新数组中索引,并采用头插法将元素迁移到新数组中。头插法会将链表的顺序翻转,这也是形成死循环的关键点。如何造成死循环以及数据丢失的。
(2)、数组扩容时如何造成死循环和数据丢失?
我们假设现在有两个线程A、B同时对下面这个HashMap进行扩容操作:
正常扩容后的结果
但是当线程A执行到上面transfer函数的第11行代码时,CPU时间片耗尽,线程A被挂起。即如下图中位置所示:
此时线程A中:e=3、next=7、e.next=null
当线程A的时间片耗尽后,CPU开始执行线程B,并在线程B中成功的完成了数据迁移
根据JMM可知,线程B执行完数据迁移后,此时主内存中newTable和table都是最新的,也就是说:7.next=3、3.next=null
随后线程A再次获得CPU时间片继续执行newTable[i] = e,将3放入新数组对应的位置,执行完此轮循环后线程A的情况如下:
接着继续执行下一轮循环,此时e=7,从主内存中读取e.next时发现主内存中7.next=3,此时next=3,并将7采用头插法的方式放入新数组中,并继续执行完此轮循环,结果如下:
上轮7.next=3,而e=3,执行下一次循环可以发现,3.next=null,所以此轮循环将会是最后一轮循环。
接下来当执行完e.next=newTable[i]即3.next=7后,3和7之间就相互连接了,当执行完newTable[i]=e后,3被头插法重新插入到链表中,执行结果如下图所示:
此时e.next=null即next=null,当执行完e=null后,将不会进行下一轮循环。到此线程A、B的扩容操作完成,很明显当线程A执行完后,HashMap中出现了环形结构,当在以后对该HashMap进行操作时会出现死循环。同时元素5在扩容期间,发生了数据丢失的问题。2、JDK1.8中的数据覆盖
(1)dk1.7的数据丢失、死循环问题在JDK1.8中已经得到了很好的解决,直接在HashMap的resize()中完成了数据迁移。
(2)为什么说 JDK1.8会出现数据覆盖的情况?
查看这段JDK1.8中的put操作代码:
如下图框中的代码是判断是否出现hash碰撞,假设两个线程A、B都在进行put操作,并且hash函数计算出的插入下标是相同的,当线程A执行完该行判断代码后由于时间片耗尽导致被挂起,而线程B得到时间片后在该下标处插入了元素,完成了正常的插入,然后线程A获得时间片,由于之前已经进行了hash碰撞的判断,所有此时不会再进行判断,而是直接进行插入,这就导致了线程B插入的数据被线程A覆盖了,从而线程不安全。
除此之外,下图pullVal方法中还有框中代码的有个++size语句,如果还是线程A、B,这两个线程同时进行put操作时,假设当前HashMap的size大小为10,当线程A执行到第38行代码时,从主内存中获得size的值为10后准备进行+1操作,但是由于时间片耗尽只好让出CPU,线程B快乐的拿到CPU还是从主内存中拿到size的值10进行+1操作,完成了put操作并将size=11写回主内存,然后线程A再次拿到CPU并继续执行(此时size的值仍为10),当执行完put操作后,还是将size=11写回内存,此时,线程A、B都执行了一次put操作,但是size的值只增加了1,所有说还是由于数据覆盖又导致了线程不安全。
二、HashMap线程安全的解决方案
1.Hashtable(废弃)
不建议使用HashTable,Oracle官方也将其废弃,建议在多线程环境下使用ConcurrentHashMap类。
(1)Hashtable的操作
HashTable的操作几乎和HashMap一致,主要的区别在于HashTable为了实现多线程安全,在几乎所有的方法上都加上了synchronized锁(锁的是类的实例,也就是整个map结构),当一个线程访问 Hashtable 的同步方法时,其他线程如果也要访问同步方法,会被阻塞住。举个例子,当一个线程使用 put 方法时,另一个线程不但不可以使用 put 方法,连 get 方法都不可以,而加锁的结果就是HashTable操作的效率十分低下。(2)HashTable与HashMap对比
①线程安全
HashMap是线程不安全的类,多线程下会造成并发冲突,但单线程下运行效率较高;
HashTable是线程安全的类,很多方法都是用synchronized修饰,但同时因为加锁导致并发效率低下,单线程环境效率也十分低;②插入null
HashMap最多允许有一个键为null,允许多个值为null;
HashTable不允许键或值为null;③容量
HashMap底层数组长度必须为2的幂(16,32,128…),这样做是为了hash准备,默认为16;
HashTable底层数组长度可以为任意值,这就造成了hash算法散射不均匀,容易造成hash冲突,默认为11;④Hash映射
HashMap的hash算法通过非常规设计,将底层table长度设计为2的幂,使用位与运算代替取模运算,减少运算消耗;// HashMap static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); } // 下标index运算 int index = (table.length - 1) & hash(key)
HashTable的hash算法首先使得hash值小于整型数最大值,再通过取模进行散射运算;
// HashTable int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length;
⑤扩容机制
HashMap创建一个为原先2倍的数组,然后对原数组进行遍历以及rehash(再次求桶);
HashTable扩容将创建一个原长度2倍的数组,再使用头插法将链表进行反序;⑥结构区别
HashMap是由数组+链表形成,在JDK1.8之后链表长度大于8时转化为红黑树;
HashTable一直都是数组+链表;⑦继承关系
HashTable继承自Dictionary类;
HashMap继承自AbstractMap类;⑧迭代器
HashMap的 Iterator 迭代器是fail-fast;fail-fast(快速失败机制):当我们在遍历集合元素的时候,经常会使用迭代器,但在迭代器遍历元素的过程中,如果集合的结构被修改(增加、删除),就会抛出Concurrent Modification Exception(并发修改异常),防止继续遍历。
fail-safe(安全失败机制):当集合的结构被改变的时候,fail-safe机制会在复制原集合的一份数据出来,然后在复制的那份数据遍历。因此,fail-safe不会抛出异常,但存在以下缺点:①复制时需要额外的空间和时间上的开销。②不能保证遍历的是最新内容。
HashTable的 Enumerator不是。
2.Collections.synchronizedMap(一般不用)
通过Collections.synchronizedMap()返回一个新的Map的实现
Map<String,String> map = Collections.synchronizedMap(new HashMap<>());
我们在调用上面这个方法的时候就需要传入一个Map,如下图可以看到有两个构造器,如果你传入了mutex参数,则将对象排斥锁赋值为传入的对象。
如果没有,则将对象排斥锁赋值为this,即调用synchronizedMap的对象,就是上面的Map
通过Collections.synchronizedMap()来封装所有不安全的HashMap的方法。
封装的关键点有2处
1 )使用了经典的synchronized来进行互斥
2)使用了代理模式new了一个新的类,这个类同样实现了Map接口.在Hashmap上面,synchronized锁住的是对象,所以第一个申请的得到锁,其他线程将进入阻塞,等待唤醒优点:代码实现十分简单,一看就懂
缺点:从锁的角度来看,基本上是锁住了尽可能大的代码块.性能会比较差.
3.ConcurrentHashMap(常用)
(1)JDK 1.7 中,采用分段锁的机制,实现并发的更新操作,底层采用数组+链表的存储结构,包括两个核心静态内部类 Segment 和 HashEntry。
①、Segment 继承 ReentrantLock(重入锁) 用来充当锁的角色,每个 Segment 对象守护每个散列映射表的若干个桶;
②、HashEntry 用来封装映射表的键-值对;
③、每个桶是由若干个 HashEntry 对象链接起来的链表
分段锁:Segment数组中,一个Segment对象就是一把锁,对应一个HashEntry数组,该数组中的数据同步依赖于同一把锁,不同HashEntry数组的读写互不干扰
jdk 1.7源码分析
ConcurrentHashMap继承了AbstractMap类,并且实现了ConcurrentMap接口,并且线程安全的实现了接口中的增删改查的方法
ConcurrentMap接口中的方法声明
ConcurrentMap中的静态变量
ConcurrentMap中的成员变量
静态内部类HashEntry
静态内部类Segment
静态内部类Segment-成员方法
ConcurrentHashMap的构造器
put方法
调用segment的put方法,首先第一步的时候会尝试获取锁,如果获得锁则node=null
获取失败肯定就有其他线程存在竞争,则利用 scanAndLockForPut() 自旋获取锁
扩容操作,在concurrentHashMap中是被保护的
当 put 方法时,发现元素个数超过了阈值,则会扩容。需要注意的是,每个Segment只管它自己的扩容,互相之间并不影响,只要是2的n次幂)。get 方法的逻辑,需要将 Key 通过 Hash 之后定位到具体的 Segment ,再通过一次 Hash 定位到具体的元素上
由于 HashEntry 中的 value 属性是用 volatile 关键词修饰的,保证了内存可见性,所以每次获取时都是最新值。缺点:jdk1.7版本虽然可以支持每个Segment并发访问,但是基本上还是数组加链表的方式,当执行查询的时候,还得遍历链表,会导致效率很低.
(2)JDK 1.8中抛弃了原有的 Segment 分段锁,来保证采用Node + CAS + Synchronized来保证并发安全性。取消类 Segment,直接用table 数组存储键值对;当 Node对象组成的链表长度超过TREEIFY_THRESHOLD 时,链表转换为红黑树,提升性能。底层变更为数组 + 链表 + 红黑树。
首先Node代替了1.7之中的HashEntry,并在val和next添加了volatile,保证了原子的可见性,也引入了红黑树,在链表大于一定值的时候会转换(默认是8)。
ConcurrentHashMap的put方法
CAS以及ABA问题CAS性能很高,但synchronized之前一直都是重量级的锁,jdk1.8 引入了synchronized,采用锁升级的方式。
针对 synchronized 获取锁的方式,JVM 使用了锁升级的优化方式,就是先使用偏向锁优先同一线程然后再次获取锁,如果失败,就升级为 CAS 轻量级锁,如果失败就会短暂自旋,防止线程被系统挂起。最后如果以上都失败就升级为重量级锁。
偏向锁:为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行,始终只有一个线程在执行同步块,在它没有执行完释放锁之前,没有其它线程去执行同步。
轻量级锁:当有两个线程,竞争的时候就会升级为轻量级锁。引入轻量级锁的主要目的是在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗。
重量级锁:大多数情况下,在同一时间点,常常有多个线程竞争同一把锁,悲观锁的方式,竞争失败的线程会不停的在阻塞及被唤醒态之间切换,代价比较大ConcurrentHashMap的get方法
-
LinkedHashMap线程不安全解决
2022-02-17 20:14:41LinkedHashMap线程不安全解决 -
解决多线程并发安全问题
2019-02-16 20:16:49解决多线程的并发安全问题,java无非就是加锁,具体就是两个方法 (1) Synchronized(java自带的关键字) (2) lock 可重入锁 (可重入锁这个包java.util.concurrent.locks 底下有两个接口,分别对应两个类实现了这个... -
JAVA多线程不安全问题解决方案(多线程并发同一资源)。
2017-04-24 10:28:58多线程同时执行的时候可能出现不安全问题 当3个人同时拿到一个苹果,他们的编号就一样,当时主要看是谁先吃掉苹果 除非拿到苹果和吃掉苹果是连续同步执行,没有其他的线程干扰 方案一: 设置同步代码块(同步锁)。... -
解决线程不安全的list的问题(Vector、Collections、CopyOnWriteArrayList)
2022-01-02 11:03:30解决线程不安全的list的问题(Vector、Collections、CopyOnWriteArrayList) -
java的SimpleDateFormat线程不安全出问题了,虚竹教你多种解决方案
2021-08-05 17:12:14我们知道SimpleDateFormat是线程不安全,本文会介绍多种解决方案来保证线程安全。 -
java多线程会造成线程安全问题的原因总结
2021-09-30 22:13:28众所周知,多线程会造成线程安全问题,那么多线程为什么会导致线程安全问题呢? 一:首先了解jvm内存的运行时数据区 1.堆区:存储对象实例(和实例变量),数组等 2.java虚拟机栈(方法·栈),存放方法声明,... -
单例模式多线程下不安全
2019-04-23 00:20:14单例模式多线程下不安全 大厂面试题: 1、请你谈谈对volatile的理解? 2、CAS你知道吗? 3、原子类AtomicInteger的ABA问题谈谈?原子更新引用知道吗? 4、我们都知道ArrayList是线程不安全的,请编码写一个不... -
高并发环境下,解决多线程线程安全和数据顺序性问题
2022-03-24 17:00:41然后将数据收集,再由多线程使用websocket实时推送给客户端 比如 推送用户关注商品的相关信息。 2、最初实现 每个topic(trade,broker等)接收程序起单独的线程,在每个线程中将数据数据整理,当有用户登录的... -
HashMap线程不安全原因及解决
2020-03-27 15:09:581、put的时候导致的多线程数据不一致。 这个问题比较好想象,比如有两个线程A和B,首先A希望插入一个key-value对到HashMap中,首先计算记录所要落到的桶的索引坐标,然后获取到该桶里面的链表头结点,此时线程A的... -
HashMap为什么线程不安全?
2021-02-04 17:01:161、HashMap线程不安全原因: 原因: JDK1.7 中,由于多线程对HashMap进行扩容,调用了HashMap#transfer(),具体原因:某个线程执行过程中,被挂起,其他线程已经完成数据迁移,等CPU资源释放后被挂起的线程重新... -
多线程引发的线程安全及处理
2022-02-13 22:17:39多线程,线程安全,死锁,线程通信 -
ArrayList线程不安全原因、复现以及解决办法
2022-01-18 21:21:55因为他的读写方法没有同步策略,会导致脏数据和不可预期的结果。 // 非原子方法,并且没有同步策略 public class ArrayList<E> { public E get(int index) { rangeCheck(index); return elementData... -
SimpleDateFormat类到底为啥不是线程安全的?(附六种解决方案,建议收藏)
2021-07-16 08:13:41SimpleDateFormat类不是线程安全的根本原因和解决方案,冰河吐血整理,建议收藏!! -
如何应对HashMap线程不安全的问题?
2019-06-12 23:02:35举个例子,当一个线程使用put方法时,另一个线程不但不可以使用put方法,连get方法都不可以。效率很低,所以都不会用。 Hashtable内方法上使用了synchronized。 2、类ConcurrentHashMap定义Map 源码是: ... -
Java中线程安全和线程不安全解析和示例
2020-05-16 19:38:25本文作为多线程编程的第一篇文章,将从一个简单的例子开始,带你真正从...文章中将提供一个完整的线程不安全示例,希望你可以跟随文章,自己真正动手运行一下此程序,体会一下多线程编程中必须要考虑的线程安全问题。 -
Java之——SimpleDateFormat 线程不安全问题及解决方法
2016-12-18 15:54:52今天,给大家带来一篇Java SimpleDateFormat在多线程环境下不安全的文章,Java SimpleDateFormat 是线程不安全的,当在多线程环境下使用一个DateFormat的时候是有问题的,如下面的例子 package com.lya.date; import... -
4种解决线程安全问题的方式
2020-05-13 10:07:50当然关于多线程的问题在面试的时候也是出现频率比较高的。下面就来学习一下吧! 线程 先来看看什么是进程和线程? 进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个... -
Java多线程之集合类(线程安全和不安全)
2019-05-14 15:26:27Java多线程之集合类(浅析线程安全和不安全) 本文目录: 1.线程不安全之ArrayList,HashSet,HashMap和线程安全之CopyOnWriteArrayList,CopyOnWriteArraySet,ConcurrentHashMap 2. 小结 3.解析... -
Java多线程之线程安全问题
2022-03-31 11:02:50本篇文章介绍的内容为Java多线程中的线程安全问题,此处的安全问题并不是指的像黑客入侵造成的安全问题,线程安全问题是指因多线程抢占式执行而导致程序出现bug的问题。 -
java 多线程 线程安全及非线程安全的集合对象
2018-09-05 10:04:42线程安全:就是当多线程访问时,采用了加锁的机制;即当一个线程访问该类的某个数据时,会对这个数据进行保护,其他线程不能对其访问,直到该线程读取完之后,其他线程才可以使用。防止出现数据不一致或者数据被污染... -
Python线程安全问题及解决方法
2019-06-08 00:11:38Python线程安全问题及解决方法 Python多线程是通过threading模块来实现的。 参考:https://mp.csdn.net/postedit/91069618 一、多线程共享全局变量 from threading import Thread list_a = [1, 2, 3] def ... -
HashMap 是线程安全的吗,为什么不是线程安全的(最好画图说明多线程环境下不安全)?
2022-02-24 13:49:01不是线程安全的; 如果有两个线程A和B,都进行插入数据,刚好这两条不同的数据经过哈希计算后得到的哈希码是一样的,且该位 置还没有其他的数据。所以这两个线程都会进入我在上面标记为1的代码中。假设一种情况... -
多线程调用static方法线程安全问题
2021-11-10 15:44:42最近在工作中遇到了线程安全的问题,是在一个方法中调用了静态方法解析Date的字符串。 因为 SimpleDateFormat这个类是线程不安全的,所以不能在静态方法中定义全局的成员变量。 @Test void contextLoads() { ... -
多线程情况下如何保证线程安全
2020-05-01 15:50:18按照“线程安全”的安全程度由强到弱来排序,我们可以将java语言中各种操作共享的数据分为以下5类:不可变、绝对线程安全、相对线程安全、线程兼容和线程对立。 1、不可变 在java语言中,不可变的对象一定是线程... -
多线程教程(二十三) 无锁实现线程安全
2022-01-19 15:52:48多线程教程(二十三) 无锁实现线程安全 题目描述: 总额10000元,1000个人取钱,每人取10块,取完余额刚好为0. 加锁方法 class AccountUnsafe implements Account { private Integer balance; public ... -
如何解决线程安全问题
2019-04-14 15:52:37当多个线程共享一个全局变量,对其做写操作时,可能会受到其他线程的干扰,从而引发线程安全问题 内置锁(synchronized) 内置锁也叫互斥锁,可以保证线程的原子性,当线程进入方法时,会自动获得一个锁,一旦锁被...