【来自B站JUC视频:https://www.bilibili.com/video/BV14W411u7gB】尚硅谷JUC源码讲授实战教程完整版(java juc线程精讲)
1、Java JUC 简介
在JDK5提供了 java.util.concurrent (简称 JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。 提供可调的、灵活的线程池。还提供了设计用于 多线程上下文中的 Collection 实现等。
2、volatile 关键字-内存可见性
JVM为每一个线程提供一个独立的缓存,用于提高效率。这个缓存,是每个线程私有的,称之为“本地内存”。内存可见性(Memory Visibility),是指当一个线程修改了某个共享变量的值之后,其他线程要能够立即知道这个修改。可见性错误,是指当读操作与写操作处在不同的线程中执行时,我们无法确保执行读操作的线程能实时看到其他写线程新写入的值。我们可以通过同步来保证对象被安全地发布。除此之外,我们也可以使用一种更加轻量级的volatile变量。Java 提供了一种稍弱的同步机制,即 volatile 变量,用来确保将变量的更新操作能够立即通知到其他线程。
可以将 volatile 看做一个轻量级的锁,但是又与锁有些不同:
(1)对于多线程,它不是一种互斥关系。(2)它只保证了“内存可见性”,不能够保证变量状态的 “原子性操作”。
public class VolatileTest {
public static void main(String[] args) {
ThreadDemo t = new ThreadDemo();
new Thread(t).start();
while (true) {
if(t.isFlag()) {
System.out.println("------");
break;
}
}
}
}
class ThreadDemo implements Runnable {
private boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("flag = " + isFlag());
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
mian线程读取到的flag 一直都是false,所以打印结果为 flag = true,然后程序没有结束。
解决:volatile,当多个线程进行操作共享数据时,可以保证内存中的数据可见性。
public class VolatileTest {
public static void main(String[] args) {
ThreadDemo t = new ThreadDemo();
new Thread(t).start();
while (true) {
if(t.isFlag()) {
System.out.println("------");
break;
}
}
}
}
class ThreadDemo implements Runnable {
private volatile boolean flag = false;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("flag = " + isFlag());
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
3、原子变量-CAS算法
类 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 的实例各自提供对 相应类型单个变量的访问和更新。每个类也为该类型提供适当的实用工具方法。
AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 类进一步扩展了原子操 作,对这些类型的数组提供了支持。这些类在为其数组元素提供 volatile 访问语义方 面也引人注目,这对于普通数组来说是不受支持的。
核心方法:boolean compareAndSet(expectedValue, updateValue)
java.util.concurrent.atomic 包下提供了一些原子操作的常用类: AtomicBoolean 、AtomicInteger 、AtomicLong 、 AtomicReference AtomicIntegerArray 、AtomicLongArray ,AtomicMarkableReference ,AtomicReferenceArray ,AtomicStampedReference
具体的一些方法,可查看API文档【https://www.matools.com/api/java8】。
i++ 原子性问题,先读取到i 然后再 ++ ,操作被分开了,有同步安全问题
public class AtomicTest {
public static void main(String[] args) {
Atomic a = new Atomic();
for (int i = 0; i < 10; i++) {
new Thread(a).start();
}
}
}
class Atomic implements Runnable {
private volatile int serialNumber = 0;
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + getSerialNumber());
}
public int getSerialNumber() {
return serialNumber++;
}
}
原子变量JDK5后 java.util.concurrent.atomic包下提供了常用的原子变量。
(1)volatile 保证内存可见性
(2)CAS算法 保证数据的原子性
CAS算法是硬件对于并发操作共享数据的支持:包含了三个操作数:内存值 V、预估值 A、更新值 B:当且仅当 V == A时,V = B,否则,将不做任何操作。
public class CompareAndSwapTest {
public static void main(String[] args) {
CompareAndSwap cas = new CompareAndSwap();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
int expectedValue = cas.get();
boolean b = cas.compareAndSet(expectedValue, (int)(Math.random() * 101));
System.out.println(b);
}
}).start();
}
}
}
class CompareAndSwap {
private int value;
// 获取内存值
public synchronized int get(){
return value;
}
// 比较
public synchronized int compareAndSwap(int expectedValue, int newValue) {
int oldValue = value;
if(oldValue == expectedValue) {
this.value = newValue;
}
return oldValue;
}
// 设置
public synchronized boolean compareAndSet(int expectedValue, int newValue) {
return expectedValue == compareAndSwap(expectedValue, newValue);
}
}
public class CopyOnWriteArrayListTest {
public static void main(String[] args) {
HelloThread ht = new HelloThread();
for (int i = 0; i < 2; i++) {
new Thread(ht).start();
}
}
}
/**
* CopyOnWriteArrayList写入并复制,添加操作多时,效率低,因为每次添加时都会进行复制,开销很大
* 并发迭代操作多时可以选择
*/
class HelloThread implements Runnable {
// 这种会出现并发修改异常
// private static List<String> list = Collections.synchronizedList(new ArrayList<String>());
private static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
static {
list.add("AA");
list.add("BB");
list.add("CC");
}
@Override
public void run() {
Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
list.add("AA");
}
}
}
public class CountDownLatchTest {
public static void main(String[] args) {
// 5 表示其他线程的数量
CountDownLatch latch = new CountDownLatch(5);
LatchDemo ld = new LatchDemo(latch);
long start = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
new Thread(ld).start();
}
try {
// 此处要一直等到 latch的值为0 ,就能往下执行了
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("消耗时间为:" + (end - start));
}
}
class LatchDemo implements Runnable {
private CountDownLatch latch;
public LatchDemo(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
synchronized(this) {
try {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
} finally {
latch.countDown();
}
}
}
}