原子性是指在一次操作或多次操作中,要么所有的操作全部都得到了执行并不会受到任何因素的干扰而中断,要么所有的操作都不执行,多个操作是一个不可分割的整体
volatile关键字
因为堆内存是唯一的而每一个线程都有自己的线程栈
每一个线程栈在使用堆内存里面的变量时都会拷贝到变量的副本中
在线程中每一次使用是从变量副本中获取的
volatile关键字是强制线程每次使用时都会看一下共享区的最新值
synchronized
synchronized锁解决流程
1线程获得锁
2清空变量副本
3拷贝共享变量最新的值到变量副本中
4执行代码
4将修改后的变量副本的值赋值给共享数据
6释放锁
AtomicInteger
自增(++)不是一个原子性的操作,所以JDK5之后提供了AtomicInteger
// 初始化一个默认值为0的原子型Integer AtomicInteger ac1 = new AtomicInteger(); // 初始化一个指定值的的原子型Integer,此处指定值为10 AtomicInteger ac2 = new AtomicInteger(10); // int get(): 获取值 int i = ac2.get(); // int getAndIncrement(): 以原子方式将当前值加1,注意,这里返回的是自增前的值。 int andIncrement = ac1.getAndIncrement(); // int incrementAndGet(): 以原子方式将当前值加1,注意,这里返回的是自增后的值。 int i1 = ac2.incrementAndGet(); // int addAndGet(int data): 以原子方式将参数与对象中的值相加,并返回结果。 int i2 = ac2.addAndGet(20); // int getAndSet(int value): 以原子方式设置为newValue的值,并返回旧值。 int andSet = ac2.getAndSet(10);
AtomicInteger原理
AtomicInteger原理是:自旋锁+CAS算法
CAS算法:
有三个操作的数:内存值,旧的值A,要修改的值B
当旧的值A==内存值,修改成功,将内存值改为新的值B
当旧的值A!=内存值,修改失败,不做任何操作,并重新获取现在的最新值
重新获取最新值的动作就是自旋
悲观锁和乐观锁
悲观锁:每次操作共享数据前都会上锁
乐观锁:修改共享数据时会检查数据是否被修改,如被修改就再次获取新值,没被修改直接修改共享数据的值
synchronized和CAS的区别:
synchronized是悲观锁
CAS是乐观锁
HashTable
在集合类中HashMap虽然常用但他是线程不安全的集合,多线程环境下可能会出现问题。为保证数据安全性可以使用HashTable,但HashTable操作数据的时候会将整张表锁起来,所以效率较低
ConcurrentHashMap
JDK5后提供的此类,但在JDK7和JDK8的两个版本中的底层原理不一样
底层原理:
空参构造创建ConcurrentHashMap如果什么都不操作,在第一次添加元素时会创建哈希表
计算当前元素应存入的索引
如果该索引位置为null,则利用cas算法将本节点添加到数组中
如果该索引为不为null,则利用volatile关键字获取当前位置最新地址值,挂载添加变成链表
当链表长度>=8时会自动转换为红黑树,以链表或红黑树头节点为锁对象,配合悲观锁保证多线程操作集合数据时的安全性