在Java 5.0 提供了java.util.concurrent(简称JUC )包,在此包中增加了在并发编程中很常用的实用工具类,用于定义类似于线程的自定义子系统,包括线程池、异步IO 和轻量级任务框架。提供可调的、灵活的线程池。还提供了设计用于多线程上下文中的Collection 实现等。
一、volatile 关键字:
当多个线程进行操作共享数据时,可以保证内存中的数据可见。
相较于 synchronized 是一种较为 轻量级的 同步策略。
注意:
1、volatile 不具备“互斥性”
2、volatile 不能保证变量的“原子性”
package com.goldencis.juc;
/**
* @author wtt
* @date 2020/2/11 14:52
* @description
* 一、volatile 关键字: 当多个线程进行操作共享数据时,可以保证内存中年的数据可见。
* 相较于 synchronized 是一种较为 轻量级的 同步策略。
* 注意:
* 1、volatile 不具备“互斥性”
* 2、volatile 不能保证变量的“原子性”
**/
public class TestJuc {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo).start();
while (true) {
// synchronized (threadDemo){
if (threadDemo.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;
}
}
二、原子变量--CAS算法
(一)、i++的原子性问题: i++的操作 实际上分为 三个步骤“读 - 改 - 写”
int i = 10;
i = i++; //10
int temp = i;
i = i + 1;
i = temp;
(二)、原子变量: jdk 1.5 后 java.util.concurrent.atomic 包下提供了常用的 原子变量;
1、volatile 保证内存可见性;
2、CAS (Compare - And - Swap)算法保证数据的原子性
CAS 算法 是硬件对于并发操作共享数据的支持;
CAS包含了 三个操作数:
内存值V
预估值A
更新值B
当且仅当 V == A时, V = B; 否则,什么操作都不做。
package com.goldencis.juc;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author wtt
* @date 2020/2/11 17:00
* @description
*
* 一、i++的原子性问题: i++的操作 实际上分为 三个步骤“读 - 改 - 写”
* int i = 10;
* i = i++; //10
*
* int temp = i;
* i = i + 1;
* i = temp;
*
* 二、原子变量: jdk 1.5 后 java.util.concurrent.atomic 包下提供了常用的 原子变量;
* 1、volatile 保证内存可见性;
* 2、CAS (Compare - And - Swap)算法保证数据的原子性
* CAS 算法 是硬件对于并发操作共享数据的支持;
* CAS包含了 三个操作数:
* 内存值V
* 预估值A
* 更新值B
* 当且仅当 V == A时, V = B; 否则,什么操作都不做。
*
**/
public class TestAtomic {
public static void main(String[] args) {
AtomicDemo atomicDemo = new AtomicDemo();
for (int i=0; i <10; i++){
new Thread(atomicDemo).start();
}
}
}
class AtomicDemo implements Runnable{
// private volatile int seriaNumber = 0;
private AtomicInteger atomicInteger = new AtomicInteger();
@Override
public void run() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println( getSeriaNumber() );
}
public int getSeriaNumber() {
// return seriaNumber ++;
return atomicInteger.getAndIncrement();
}
}
三、ConcurrentHashMap 锁分段机制
CopyOnWriteArrayList / CopyOnWriteArraySet :“写入并复制”
注意: 添加操作多时,效率低,因为每次添加时都会进行复制,开销非常的大。并发迭代操作时可以选择。
四、CountDownLatch 闭锁
CountDownLatch :闭锁,在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才能继续执行。
1、子线程 latch.countDown();
2、主线程 countDownLatch.await();
package com.goldencis.juc;
import java.util.concurrent.CountDownLatch;
/**
* @author wtt
* @date 2020/2/12 10:28
* @description
* CountDownLatch :闭锁,在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才能继续执行
**/
public class TestCountDownLatch {
public static void main(String[] args) {
final CountDownLatch countDownLatch = new CountDownLatch(10);
LatchDemo latchDemo = new LatchDemo(countDownLatch);
long start = System.currentTimeMillis();
for (int i=0; i<10; i++){
new Thread(latchDemo).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("消耗时间:"+ (end - start) );
}
}
class LatchDemo implements Runnable{
CountDownLatch latch;
public LatchDemo(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
synchronized (this){
try{
for (int i=0; i < 5000; i++){
if (i % 2 ==0 ){
System.out.println(i);
}
}
}finally {
latch.countDown();
}
}
}
}
五、实现Callable 接口
(一)、创建执行线程的方式三:实现Callable接口。相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
(二)、执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask 是 Future 接口的实现类。
FutureTask 可用于闭锁
package com.goldencis.juc;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @author wtt
* @date 2020/2/12 14:17
* @description
* 一、创建执行线程的方式三:实现Callable接口。相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
* 二、执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。 FutureTask 是 Future 接口的实现类。
**/
public class TestCallable {
public static void main(String[] args) {
CallableDemo callableDemo = new CallableDemo();
//1、执行 Callable 方式,需要 FutureTask 实现类的支持,用于接收运算结果。
FutureTask<Integer> result = new FutureTask<>(callableDemo);
new Thread(result).start();
//2、接收线程运算后的结果。
try {
Integer sum = result.get(); // FutureTask 可用于闭锁
System.out.println(sum);
System.out.println("----------------------------------");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
class CallableDemo implements Callable<Integer>{
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100000; i ++){
sum += i;
}
return sum;
}
}
六、Lock 同步锁
(一)、用于解决多线程安全问题的方式:
synchronized: 隐式锁
1、同步代码块
2、同步方法
jdk 1.5 后:
3、同步锁Lock
注意: 是一个显示锁,需要通过 lock() 方法上锁,必须通过 unlock()方法进行释放锁
package com.goldencis.juc;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author wtt
* @date 2020/2/12 16:07
* @description
* 一、用于解决多线程安全问题的方式:
* synchronized: 隐式锁
* 1、同步代码块
* 2、同步方法
*
* jdk 1.5 后:
* 3、同步锁Lock
* 注意: 是一个显示锁,需要通过 lock() 方法上锁,必须通过 unlock()方法进行释放锁
**/
public class TestLock {
public static void main(String[] args) {
LockDemo lockDemo = new LockDemo();
new Thread(lockDemo,"1号窗口").start();
new Thread(lockDemo,"2号窗口").start();
new Thread(lockDemo,"3号窗口").start();
}
}
class LockDemo implements Runnable{
int ticket = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
try{
if (ticket > 0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "完成售票, 余票为 : " + --ticket );
}
}finally {
lock.unlock();
}
}
}
}
七、Condition 控制线程通信
(一)生产者消费者问题
1、synchronized 与 wait()、notifyAll()联合使用。
为了避免虚假唤醒问题,应该总是使用在循环中(while)
2、Lock 与 Condition 联合使用问题。
Lock.lock() 、unlock()方法;
Condition.await()、 signalAll()方法;
package com.goldencis.juc;
/**
* @author wtt
* @date 2020/2/13 9:52
* @description
* 生产者消费者问题
**/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor productor = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(productor, "生产者A").start();
new Thread(consumer, "消费者B").start();
new Thread(productor, "生产者C").start();
new Thread(consumer, "生产者D").start();
}
}
//店员
class Clerk {
private int product = 0;
//生产
public synchronized void get(){
while (product >= 1 ){ //为了避免虚假唤醒问题,应该总是使用在循环中
System.out.println("产品已满!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " :" + ++product);
this.notifyAll();
}
//消费
public synchronized void sale(){
while (product <= 0 ){
System.out.println("缺货!");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": " + --product);
this.notifyAll();
}
}
/**
* 生产者
*/
class Productor implements Runnable{
Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for(int i = 0 ; i < 20 ; i ++){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable{
Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for(int i = 0 ; i < 20 ; i ++){
clerk.sale();
}
}
}
package com.goldencis.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author wtt
* @date 2020/2/13 9:52
* @description
* 生产者消费者问题
**/
public class TestProductorAndConsumerForLock {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor productor = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(productor, "生产者A").start();
new Thread(consumer, "消费者B").start();
new Thread(productor, "生产者C").start();
new Thread(consumer, "生产者D").start();
}
}
//店员
class Clerk {
private int product = 0;
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
//生产
public void get(){
lock.lock();
try {
while (product >= 1 ){ //为了避免虚假唤醒问题,应该总是使用在循环中
System.out.println("产品已满!");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " :" + ++product);
condition.signalAll();
}finally {
lock.unlock();
}
}
//消费
public void sale(){
lock.lock();
try {
while (product <= 0) {
System.out.println("缺货!");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": " + --product);
condition.signalAll();
}finally {
lock.unlock();
}
}
}
/**
* 生产者
*/
class Productor implements Runnable{
Clerk clerk;
public Productor(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for(int i = 0 ; i < 20 ; i ++){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable{
Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
for(int i = 0 ; i < 20 ; i ++){
clerk.sale();
}
}
}
八、线程按序交替
编写一个程序,开启3 个线程,这三个线程的ID 分别为A、B、C,每个线程将自己的ID 在屏幕上打印10 遍,要求输出的结果必须按顺序显示。
如:ABCABCABC…… 依次递归
package com.goldencis.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author wtt
* @date 2020/2/13 14:33
* @description
* ABC依次排序
**/
public class TestABCAlternate {
public static void main(String[] args) {
final AlternateDemo alternateDemo = new AlternateDemo();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1 ; i <= 20; i ++){
alternateDemo.loopA(i);
}
}
},"A").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1 ; i <= 20; i ++){
alternateDemo.loopB(i);
}
}
},"B").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 1 ; i <= 20; i ++){
alternateDemo.loopC(i);
System.out.println("----------------------------------------------");
}
}
},"C").start();
}
}
class AlternateDemo {
private int num = 1;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void loopA(int totalLoop){
lock.lock();
try {
//1、判断
if (num != 1){
condition1.await();
}
//2、打印
for(int i = 1; i <= 5; i++){
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
//3、唤醒
num = 2;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void loopB(int totalLoop){
lock.lock();
try {
//1、判断
if (num != 2){
condition2.await();
}
//2、打印
for(int i = 1; i <= 15; i++){
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
//3、唤醒
num = 3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void loopC(int totalLoop){
lock.lock();
try {
//1、判断
if (num != 3){
condition3.await();
}
//2、打印
for(int i = 1; i <= 20; i++){
System.out.println(Thread.currentThread().getName() + "\t" + i + "\t" + totalLoop);
}
//3、唤醒
num = 1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
九、ReadWriteLock 读写锁
写写/ 读写 需要“互斥”
读读 不需要“互斥”
package com.goldencis.juc;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @author wtt
* @date 2020/2/14 16:49
* @description
* 1、ReadWriteLock 读写锁
* 写写/ 读写 需要“互斥”
* 读读 不需要“互斥”
**/
public class TestReadWriteLock {
public static void main(String[] args) {
ReadWriteLockDemo rw = new ReadWriteLockDemo();
new Thread(new Runnable() {
@Override
public void run() {
rw.set((int)(Math.random()* 101)) ;
}
}, "Write: ").start();
for (int i = 0 ; i <30; i++){
new Thread(new Runnable() {
@Override
public void run() {
rw.get();
}
}).start();
}
}
}
class ReadWriteLockDemo{
private int number = 0;
private ReadWriteLock lock = new ReentrantReadWriteLock();
//读
public void get(){
lock.readLock().lock(); //上锁
try{
System.out.println(Thread.currentThread().getName() + " : " + number);
}finally {
lock.readLock().unlock(); //释放锁
}
}
//写
public void set(int number){
lock.writeLock().lock(); //上锁
try{
System.out.println(Thread.currentThread().getName() + ": " + number);
this.number = number;
}finally {
lock.writeLock().unlock(); //释放锁
}
}
}
十、线程八锁
线程八锁的关键:
1、非静态方法的锁默认为 this, 静态方法的锁 对应的 Class 实例。
2、某一时刻内,只能有一个线程持有锁,无论几个方法。
题目:判断打印的是 “one” or “two”;
1、两个普通同步(synchronized)方法,两个线程,标准打印,打印? //one two
2、新增Thread.sleep() 给 getOne(),打印? // one two
3、新增普通方法 getThree(),打印? //three one two
4、两个普通同步方法,两个 Number 对象,打印? // two one
5、修改 getOne()为 静态同步方法,打印? // two one
6、修改两个方法均为静态同步方法,一个Number对象? //one two
7、一个静态同步方法,一个非静态同步方法,两个Number对象? // two one
8、两个静态同步方法,两个Number对象? //one two
十一、线程池
(一)、线程池:提供了一个线程队列,队列中保存着 所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。
(二)、线程池的体系结构:
java.util.concurrent.Executor :负责线程的使用与调度的根接口
|--**ExecutorService 子接口:线程池的主要接口
|-- ThreadPoolExecutor 线程池的实现类
|-- ScheduledExecutorService 子接口: 负责线程的调度
|-- ScheduledThreadPoolExecutor: 继承 ThreadPoolExecutor,实现 ScheduledExecutorService。
(三)、工具类: Executors
ExecutorService newFixedThreadPool() :创建固定大小的线程池
ExecutorService newCachedThreadPool(): 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
ExecutorService newSingleThreadExecutor(): 创建单个线程池。线程池中只有一个线程。
ExecutorService newScheduledThreadPool(): 创建固定大小的线程,可以延迟 或定时的执行任务。
注:ExecutorService.submit() 有两种方式, Runnable、Callable。
package com.goldencis.juc;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.function.Consumer;
import static java.util.concurrent.Executors.newFixedThreadPool;
/**
* @author wtt
* @date 2020/2/14 10:40
* @description
* (一)、线程池:提供了一个线程队列,队列中保存着 所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。
* (二)、线程池的体系结构:
* java.util.concurrent.Executor :负责线程的使用与调度的根接口
* |--**ExecutorService 子接口:线程池的主要接口
* |-- ThreadPoolExecutor 线程池的实现类
* |-- ScheduledExecutorService 子接口: 负责线程的调度
* |-- ScheduledThreadPoolExecutor: 继承 ThreadPoolExecutor,实现 ScheduledExecutorService。
* (三)、工具类: Executors
* ExecutorService newFixedThreadPool() :创建固定大小的线程池
* ExecutorService newCachedThreadPool(): 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
* ExecutorService newSingleThreadExecutor(): 创建单个线程池。线程池中只有一个线程。
* ExecutorService newScheduledThreadPool(): 创建固定大小的线程,可以延迟 或定时的执行任务。
**/
public class TestThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//1、创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
List<Future<Integer>> list = new ArrayList<>();
for(int i = 0; i <10 ; i ++){
Future<Integer> future = executorService.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i <= 100; i ++){
sum += i;
}
return sum;
}
});
list.add(future);
}
executorService.shutdown();
list.stream().forEach(integerFuture -> {
try {
System.out.println(integerFuture.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
});
/* //2、分配线程池
ThreadPoolDemo tpd = new ThreadPoolDemo();
for(int i = 0; i < 20 ; i ++){
executorService.submit(tpd);
}
//3、关闭线程池
executorService.shutdown();*/
}
}
class ThreadPoolDemo implements Runnable{
private int i = 0;
@Override
public void run() {
while (i <= 100){
System.out.println(Thread.currentThread().getName() + ":" + i++ );
}
}
}
package com.goldencis.juc;
import javax.swing.*;
import java.util.Random;
import java.util.concurrent.*;
/**
* @author wtt
* @date 2020/2/14 11:48
* @description
**/
public class TestScheduledThreadPool {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 5; i++){
Future<Integer> future = pool.schedule(() -> {
int num = new Random().nextInt(100);
System.out.println(Thread.currentThread().getName() + ": " + num);
return num;
}, 1, TimeUnit.SECONDS);
System.out.println(future.get());
}
pool.shutdown();
}
}
注:本文全文 参考 尚学堂《JUC》课程。