JUC之自旋锁
自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好吃是减少线程上下文切换的消耗,缺点是循环会消耗CPU。
自旋的方式我们在查看AtomicInteger的底层时候就已经体验过了
123456789public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); //自旋 } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5;}
手写自旋锁首先原子引用线程类AtomicReference<Thread> atomicReference = new AtomicReference<>();
接着编写myLock()首先通过Thread.currentThread();获取到当 ...
JUC之可重入锁
可重入锁可重入锁又叫递归锁,指的是同一线程外层函数获得锁之后,内存递归函数仍然能获取该锁的代码,同一个线程在外层获取锁的时候,进入内层方法会自动获取锁。也就是说,线程可以进入任何一个它已经拥有的锁所同步者的代码块。
ReentrantLock/synchronized就是一个典型的可重入锁
代码验证synchronized
123456789101112131415161718192021222324public class ReenterLockDemo { public static void main(String[] args) { Phone phone = new Phone(); new Thread(() -> { phone.sendSMS(); }, "t1").start(); new Thread(() -> { phone.sendSMS(); }, ...
JUC之公平锁和非公平锁
概念公平锁是指多个线程按照申请锁的顺序来获取锁,采用先来后到哦,先来先服务的原则。老的线程排队使用锁,新线程仍然排队使用锁。
非公平锁多个线程按照上来就直接尝试占有锁,如果尝试失败就采用类似公平锁的方式。老的线程排队使用锁,但是无法保证新线程抢占已经在排队的线程的锁。
非公平锁的优点在于吞吐量比公平锁大。
并发包ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或者非公平锁 默认是非公平锁
实例我们经常使用到的锁是ReentrantLock我们可以查看它的源码,默认使用的是非公平锁。
也就是说ReentrantLock可自定义使用的是公平还是非公平锁,默认使用的是非公平锁。
对于synchronized而言 也是一种非公平锁.
JUC之ABA问题
什么是ABA问题?ABA问题是由CAS而导致的一个问题
CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并交换,那么在这个时间差内会导致数据的变化。
比如说一个线程一从内存位置V中取出A,这是另一个线程二也从内存中取出A,并且线程二进行类一些操作将值变成了B,然后线程二又将V位置的数据变成A,这时候线程一进行CAS操作发现内存中仍然是A,然后线程一操作成功!
尽管线程一的CAS操作成功,但是不代表这个过程就是没有问题的。
就比如你家每天,都有一个B流浪汉来你家进行生活,他每天在你回家的时候将家整理成你离开的样子,不留下痕迹。你每天回到家后没有发现什么异常于是就正常生活。虽然他目前没有对你的生活造成什么问题,但是这个过程是很有隐患的!!!
总的来说以前的CAS操作,只管过程,也就是开头和结尾是否相同,如果相同就进行操作!而不管你中间到底进行来什么操作!!
原子引用如何解决ABA问题呢?首先我们看一下原子引用。我们先前已经使用过java.util.concurrent.atomic下的基本数据类型原子操作类。但是缺少对对象的操作的原子类。也就是AtomicRefere ...
JUC之CAS原理
CAS概念CAS的英文为Compare and Swap 翻译为比较并交换。
CAS机制中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。
更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B。
CAS的全称为Compare-And-Swap ,它是一条CPU并发原语.
它的功能是判断内存某个位置的值是否为预期值,如果是则更新为新的值,这个过程是原子的.
CAS并发原语提现在Java语言中就是sun.miscUnSaffe类中的各个方法.调用UnSafe类中的CAS方法,JVM会帮我实现CAS汇编指令 .这是一种完全依赖于硬件 功能,通过它实现了原子操作,再次强调,由于CAS是一种系统原语,原语属于操作系统用于范畴,是由若干条指令组成,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许中断,也即是说CAS是一条原子指令,不会造成所谓的数据不一致的问题.
CAS应用将CAS的应用前我们要先将一个类,这个类我们已经使用过了,就是下图这个例子。
123456789101112131415161 ...
JUC之volatile详解
volatilevolatile是Java虚拟机提供的轻量级的同步机制,保证可见性,不保证原子性,禁止指令重排(保证可序性)
Java内存模型JMM
验证可见性
12345678910111213141516171819202122232425262728293031323334353637public class VolatileDemo { public static void main(String[] args) { //volatile 可以保证可见性,及时通知其他线程,主物理内存的值已经被修改 MyData myData = new MyData(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + "\t come in"); //暂停 try { //确保后面的mai ...
Java内存模型JMM
Java内存模型概述Java内存模型(即Java Memory Model,简称JMM)本身是一种抽象的概念,并不真实存在,它描述的是一组规则或规范,通过这组规范定义了程序中各个变量(包括实例字段,静态字段和构成数组对象的元素)的访问方式。
由于JVM运行程序的实体是线程,而每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),用于存储线程私有的数据,而Java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再将变量写回主内存,不能直接操作主内存中的变量,工作内存中存储着主内存中的变量副本拷贝,前面说过,工作内存是每个线程的私有数据区域,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简要访问过程如下图
主内存
主要存储的是Java实例对象,所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的本地变量(也称局部变量),当然也包括了共享的类信息、常量、 ...
JUC之集合类不安全
ArrayList首先手写一个案例来证明ArrayList不安全。
运行多次查看结果~
打印结果与我们的预期不符,list集合中添加了null。
将线程数量添加至30个
运行报java.util.ConcurrentModificationException并发修改异常
至此我们可以发现ArrayList是多线程下的不安全的。
123456789101112131415161718public class ContainerNotSafeDemo { public static void main(String[] args) { //java.util.ConcurrentModificationException //new Vector<>(); //Collections.synchronizedList(new ArrayList<>()); //new CopyOnWriteArrayList<>(); List<Stri ...
JUC之八锁问题
环境准备首先编写一个MyPhone类,编写两个同步方法sendEmail和sendEMS方法
接着在Lock8类中的main方法中,创建两个A,B线程分别调用sendEmail和sendEMS方法。由于线程创建后是有CPU调度执行的,所以A,B线程执行顺序是随机的。为了测试效果,我们让主线程睡眠200毫米,从而确保每次执行顺序都是A线程先开始调度执行。
多次运行查看效果~,结果与预期相符。
12345678910111213141516171819202122232425262728293031323334353637public class Lock8 { public static void main(String[] args) throws InterruptedException { MyPhone phone = new MyPhone(); new Thread(() -> { try { phone.sendEmail(); ...
JUC之synchronized和Lock
为了解决线程同步问题我们经常使用synchronized关键字和Lock接口下的锁来解决同步问题。但是这两者有什么区别之处呢?首先我们分别使用synchronized和Lock来解决生产者消费者问题。
生产者消费者问题当生产者生产数量为1的时候,通知消费者消费。当生产者产品数量为0的时候,生产产品。
synchronized
运行测试,结果符合预期!
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081/** * 线程之间的通信问题:生产者和消费者问题!等待唤醒,通知唤醒 * 线程交替执行 A B 操作同一个变量 num = 0 * A num+1 * B num-1 */public class A { public static void main(String[] args) { Dat ...