Java知识点.线程-线程安全

2022/1/9 14:34:13

本文主要是介绍Java知识点.线程-线程安全,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

目录

    • 线程的状态
      • 1.什么叫做线程安全
      • 2.如何实现线程安全
        • a. synchronized方法和代码块。
        • b. ThreadLocal
        • c. Local的实现类
        • d. 原子类(基于volatile和CAS机制实现的,volatile保证可见性,CAS保证原子性)
      • 3. CAS
      • 4. volatile
      • 5.jdk5提供的java.util.concurrent并发包
    • 拓张的知识点
      • 5.concurrentHashMap和HashTable和HashMap的区别
      • 4)总结
    • 7.锁的分类

线程的状态

新建(NEW Thered):
就绪(.start):获取cpu时间片才能进入运行状态
运行(.run()):
阻塞(wait() join() sleep()):(执行完睡眠时间,或者获取锁)(notify()唤醒)
死亡(正常执行结束,while(条件符合结束)):

1.什么叫做线程安全

在多线程环境下,数据更新的结果和单线程环境下的结果是一致的就说明是线程安全的。

2.如何实现线程安全

1)要保证一个线程安全,并不是一定要进行同步,如果一个方法本来就不涉及共享数据,那他自然就无需任何同步措施去保证结果正确性。
2)多个线程访问同一个方法的局部变量时,不会出现线程安全问题,因为局部变量存储在栈中,属于线程私有的。
3)使用锁

a. synchronized方法和代码块。

在这里插入图片描述

b. ThreadLocal

每个ThreadLocal可以放一个线程级别的变量,但是它本身可以被多个线程共享使用,而且又可以达到线程安全的目的,且绝对线程安全。ThreadLocalMap,键是线程,值是value.

c. Local的实现类

ReentrantLock(重入锁),ReentrantReadWriteLock(重入读写锁)
在这里插入图片描述

d. 原子类(基于volatile和CAS机制实现的,volatile保证可见性,CAS保证原子性)

AtomicInteger, AtomicLong, AtomicBoolean

3. CAS

Compare And Swap是比较与交换,是一种无锁算法。在不使用锁的情况下实现多线程之间的变量同步。
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
在这里插入图片描述

4. volatile

在这里插入图片描述
通俗的将就是如果有一个共享变量N,当有两个线程T1、T2同时获取了N的值,T1修改N的值,而T2读取N的值。那么可见性规范要求T2读取到的必须是T1修改后的值,而不能在T2读取旧值后T1修改为新值。volatile关键字修饰的共享变量可以提供这种可见性规范,也叫做读写可见。

5.jdk5提供的java.util.concurrent并发包

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

拓张的知识点

5.concurrentHashMap和HashTable和HashMap的区别

  1. HashTable(synchronized实现线程安全)
    底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
    初始size为11,扩容:newsize = oldsize*2+1
  2. HashMap( HashMap在jdk中是一个链表的闭地址散列结构。因此在高并发的情况下容易引发get与set同时出现的场景,这样就容易导致链表遍历死循环而引发并发问题。推荐在有并发读写HashMap的地方使用ConcurrentHashMap消除并发问题)
    底层数组+链表实现(+红黑树jdk8后),可以存储null键和null值,线程不安全
    初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
    扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入,插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
    当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
  3. ConcurrentHashMap(分段锁实现线程安全)
    底层采用分段的数组+链表实现,线程安全
    通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是 volatile的,也能保证读取到最新的值。)
    Hashtable的synchronized是针对整张Hash表的,即每次锁住整张表让线程独占,ConcurrentHashMap允许多个修改操作并发进行,其关键在于使用了锁分离技术
    有些方法需要跨段,比如size()和containsValue(),它们可能需要锁定整个表而而不仅仅是某个段,这需要按顺序锁定所有段,操作完毕后,又按顺序释放所有段的锁。
    扩容:段内扩容(段内元素超过该段对应Entry数组长度的75%触发扩容,不会对整个Map进行扩容),插入前检测需不需要扩容,有效避免无效扩容。

4)总结

Hashtable和HashMap都实现了Map接口,但是Hashtable的实现是基于Dictionary抽象类的。Java5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。

7.锁的分类

在这里插入图片描述
在这里插入图片描述



这篇关于Java知识点.线程-线程安全的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程