文章目录
JUC常用类介绍
Volatile
volatie只能保证线程之间的可见性,不能保证原子性。
/**
* 不能保证原子性的代码
*/
public class VolatileNotSync {
volatile int count = 0;
void m() {
for (int i = 0; i < 10000; i++) {
count++;
}
}
public static void main(String[] args) {
VolatileNotSync volatileNotSync = new VolatileNotSync();
List<Thread> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
Thread thread = new Thread(volatileNotSync::m);
list.add(thread);
}
list.forEach(Thread::start);
for (Thread thread : list) {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(volatileNotSync.count);
}
}
Synchronized
既保证了可见性,也保证了原子性。jdk1.6 Synchronized做了优化,锁升级(无锁 -> 偏向锁 -> 乐观锁 -> 重量级锁)
AtomicXXX
AtomXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性的。底层利用CAS实现
public class TestAtomic {
AtomicInteger count = new AtomicInteger();
void m() {
for (int i = 0; i < 10000; i++) {
// 多个方法连续调用,不能保证原子性
if (count.get() < 1000)
count.incrementAndGet();
}
}
public static void main(String[] args) {
TestAtomic testAtomic = new TestAtomic();
List<Thread> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
list.add(new Thread(testAtomic::m, "thread-" + i));
}
list.forEach(Thread::start);
list.forEach((o) -> {
try {
o.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
System.out.println(testAtomic.count);
}
}
Reentrantlock
可以替代Synchronized,必须手动释放锁。而且可以指定公平锁。
public class TestReentrantLock {
// true为公平锁,false为非公平锁,默认为false
private static ReentrantLock lock = new ReentrantLock(true);
void run() {
for (int i = 0; i < 100; i++) {
lock.lock();
System.out.println(Thread.currentThread().getName() + "---");
lock.unlock();
}
}
public static void main(String[] args) {
TestReentrantLock t = new TestReentrantLock();
Thread t1 = new Thread(t::run);
Thread t2 = new Thread(t::run);
t1.start();
t2.start();
}
}
CountDownLatch
java1.5被引入,使一个线程等待其他线程各自执行完毕后再执行。
通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
public class TestCountdownLatch {
public static void main(String[] args) {
usingCountDownLatch();
}
private static void usingCountDownLatch() {
Thread[] threads = new Thread[100];
CountDownLatch latch = new CountDownLatch(threads.length);
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(() -> {
int result = 0;
for (int j = 0; j < 10000; j++) result += j;
latch.countDown();
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("end latch");
}
}
CyclicBarrier
会让所有线程都等待完成后才会继续下一步行动。
比如,就像生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是 CyclicBarrier。
参考:https://www.jianshu.com/p/333fd8faa56e
CountDownLatch和CyclicBarrier区别:
- countDownLatch是一个计数器,线程完成一个记录一个,计数器递减,只能只用一次
- CyclicBarrier的计数器更像一个阀门,需要所有线程都到达,然后继续执行,计数器递增,提供reset功能,可以多次使用
public class TestCyclicBarrier {
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(20, () -> {
System.out.println(Thread.currentThread().getName() + ":完成最后任务");
});
for (int i = 0; i < 20; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "到达");
Thread.sleep(100);
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
Phaser
JAVA 1.7引入了一个新的并发API:Phaser,一个可重用的同步barrier。用来解决控制多个线程分阶段共同完成任务的情景问题。
比如:5个学生一起参加考试,一共有三道题,要求所有学生到齐才能开始考试,全部同学都做完第一题,学生才能继续做第二题,全部学生做完了第二题,才能做第三题,所有学生都做完的第三题,考试才结束。分析这个题目:这是一个多线程(5个学生)分阶段问题(考试考试、第一题做完、第二题做完、第三题做完),所以很适合用Phaser解决这个问题。
参考:https://blog.csdn.net/u010739551/article/details/51083004
ReadWriterLock
JUC包主要是 ReentrantReadWriteLock。提供 readLock 和 writeLock 方法。
- ReetrantReadWriteLock读写锁的实现中,读锁使用共享模式;写锁使用独占模式,换句话说,读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的
- ReetrantReadWriteLock读写锁的实现中,需要注意的,当有读锁时,写锁就不能获得;而当有写锁时,除了获得写锁的这个线程可以获得读锁外,其他线程不能获得读锁
- 读写锁的实现必须确保写操作对读操作的内存影响。换句话说,一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容,读写锁之间为互斥
- ReentrantReadWriteLock支持锁降级,不支持锁升级(同一个线程中,在没有释放读锁的情况下,就去申请写锁)。
public class TestReadWriteLock {
static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
static Lock readLock = lock.readLock();
static Lock writeLock = lock.writeLock();
void read() {
readLock.lock();
if (!lock.isWriteLocked()) {
System.out.println(Thread.currentThread().getName() + "获取到读锁");
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "正在读...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
readLock.unlock();
System.out.println(Thread.currentThread().getName() + "释放读锁");
}
void write() {
writeLock.lock();
if (lock.isWriteLocked()) {
System.out.println(Thread.currentThread().getName() + "获取到写锁");
}
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "正在写...");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
writeLock.unlock();
System.out.println(Thread.currentThread().getName() + " 释放写锁");
}
public static void main(String[] args) {
TestReadWriteLock t = new TestReadWriteLock();
for (int i = 0; i < 3; i++) {
new Thread(t::read).start();
}
for (int i = 0; i < 2; i++) {
new Thread(t::write).start();
}
for (int i = 0; i < 3; i++) {
new Thread(t::read).start();
}
}
}
Semaphore
Semaphore 是一个计数信号量,必须由获取它的线程释放。
常用于限制可以访问某些资源的线程数量,例如通过 Semaphore 限流。
Semaphore 只有3个操作:
- 初始化
- 增加
- 减少
public class TestSemaphore {
public static void main(String[] args) {
Semaphore s = new Semaphore(2, true);
new Thread(() -> {
try {
s.acquire();
System.out.println("t1.start");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
s.release();
}
}).start();
new Thread(() -> {
try {
s.acquire();
System.out.println("t2.start");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
s.release();
}
}).start();
}
}
Exchanger
Exchanger 是 JDK 1.5 开始提供的一个用于两个工作线程之间交换数据的封装工具类,简单说就是一个线程在完成一定的事务后想与另一个线程交换数据,则第一个先拿出数据的线程会一直等待第二个线程,直到第二个线程拿着数据到来时才能彼此交换对应数据。
public class TestExchanger {
static Exchanger<String> exchanger = new Exchanger<>();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
String t = "t1";
try {
t = exchanger.exchange(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-" + t);
});
Thread t2 = new Thread(() -> {
String t = "t2";
try {
t = exchanger.exchange(t);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-" + t);
});
t1.start();
t2.start();
}
}
LockSupport
主要有park()和unpark()方法。park()阻塞当前线程,unpark()恢复当前线程。
与wait和notify的主要区别表现在 1.不需要获取某个对象的锁。2.没有前后顺序的区别
public class TestLockSupport {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(i);
if (i == 5) {
LockSupport.park();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
try {
// 保证先park后unpark
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
LockSupport.unpark(t1);
}
}