这篇文章主要侧重讲JUC的多数类的使用,文章里贴了很多练习的代码,可以通过代码更加深刻的了解这些类的功能。
这篇文章主要总结了volatile、原子类、ReentrantLock、CountDownLatch、CyclicBarrier、Phaser、Semphore、Exchanger的使用,然后进行了一些对比。
个人博客:https://www.elltor.com/archives/110.html
欢迎访问,相互学习,共同进步👋
文章目录
volatile
volatile使用
/*
* 演示volatile的作用
* 若running不使用volatile修饰则在主线程(main)修改running=false线程不会停下,而是继续运行
*
* 结论:通过volatile修饰running可以让线程使用running时获得最新值
*
* volatile的作用:
* 1)使共享变量在线程间可见,通过JMM内存协议,CPU缓存一致性协议使缓存失效,直接读取内存中的数据
* 2)阻止指令重排序
*/
public class T_VolatileTest {
static class Task implements Runnable{
/*volatile*/ boolean running = true;
@Override
public void run() {
System.out.println("thread start");
while (running){
}
System.out.println("thread end");
}
}
public static void main(String[] args) {
Task t = new Task();
new Thread(t).start();
try {
Thread.sleep(1000); //1s
} catch (InterruptedException e) {
e.printStackTrace();
}
t.running = false;
System.out.println("set running=false");
}
}
volatile并不是锁
/*
* volatile并不能代替volatile,多并发环境下的m方法如果不同步执行,结果是错的。
* 在对m方法同步后,结果输出正确。
*
* 结论:
* 1)volatile不能够做锁
* 2)它仅具有使多线程间共享变量可见,但不具有同步的功能
*/
public class T_VolatileNotSync {
volatile int count = 0;
/*synchronized*/ void m() {
for(int i=0; i<1000; i++) count++;
}
public static void main(String[] args) {
T_VolatileNotSync t = new T_VolatileNotSync();
List<Thread> threads = new ArrayList<Thread>();
for(int i=0; i<10; i++) {
threads.add(new Thread(t::m, "thread-"+i));
}
// 启动线程
for (Thread th : threads) {
th.start();
}
// 阻塞到所有线程执行完毕
threads.forEach((o)->{
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// 输出结果
System.out.println(t.count);
}
}
volatile只能保证引用本身可见,内部数据不能保证
/*
* 在引用上t加volatile,只能保证引用(类、数组实例)本身可见,不能保证内部可见
*/
public class T_VolatileReference {
static volatile Task t = new Task();
static class Task implements Runnable{
boolean running = true;
@Override
public void run() {
System.out.println("run start");
while(running) {
}
System.out.println("run end!");
}
}
public static void main(String[] args) {
Thread thread = new Thread(t);
thread.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.running = false;
}
}
原子操作类
原子操作类使java.util.atomic
包下的类,这些类为操作提供了原子性,使用时不用进行额外的同步,这些操作仅限于对原子类对象进行操作,如果多个原子类多对象进行操作还是需要额外的同步。
AutomicInteger使用
在Counter类中AutomicInteger并没有使用volatile修饰,并且run中的integer.incrementAndGet()没有进行同步,最终这些操作并没有在多线程环境下出错。
// Atomic 操作是原子操作
public class TestAtomic {
static class Counter implements Runnable{
AtomicInteger integer = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
integer.incrementAndGet();
}
}
}
public static void main(String[] args) {
List<Thread> threadList = new ArrayList<>(10);
Counter c = new Counter();
// 10个线程对Counter进行类型,每个线程执行run会累加10000此,最终结果为10 0000
for (int i = 0; i < 10; i++) {
threadList.add(new Thread(c));
}
for (Thread thread : threadList) {
thread.start();
}
// 阻塞到所有线程执行完毕
for (Thread thread : threadList) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// result
System.out.println(c.integer.get());
}
}
使用原子类实现CAS操作。
// 使用原子类的CAS
// 使两个线程协作输出两个字符串 str1=123456 str2=ABCDEF, 要求结果输出为:1A2B3C4D5E6F
public class TestAtomic2 {
public static void main(String[] args) {
String str1 = "123456";
String str2 = "ABCDEF";
// cas 锁
AtomicInteger mutex = new AtomicInteger(0);
// CAS操作,threa1在0输出,thread2在1时输出
// thread 1
new Thread(() -> {
for (int i = 0; i < str1.length(); i++) {
// 与期望的值不符进行空循环
while (mutex.get()!=0) {
}
System.out.print(str1.charAt(i));
mutex.set(1);
}