多线程进阶==>>JUC 并发编程
1、什么是 JUC
JUC 是 java.util.concurrent 的简写,jdk 中 JUC 相关的包有三个
- java.util.concurrent
并发相关
- java.util.concurrent.atomic
原子性
- java.util.concurrent.locks
lock 锁
JUC 意思是 java 并发编程工具包,并发编程的本质就是充分利用 CPU 资源
2、线程和进程
进程:一个程序,程序的集合。
一个进程往往可以包含多个线程,至少包含一个。
java 默认有两个线程,一个 main ,一个 GC
Java 实现多线程:Thread Runnbale Callable
Java 可以开启线程吗不可以
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
// 本地方法,底层的 C++,java 无法直接操作硬件!
private native void start0();
并发和并行
并发:多线程操作同一个资源,单核 CPU,模拟多线程,快速交替
并行:多核 CPU,多个线程可以同时执行
public class Test1 {
public static void main(String[] args) {
// 获取 CPU 核数
// CPU 密集型,IO 密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
线程的状态
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
wait / sleep 的区别
1.来自不同的类
wait - > Object
sleep - > Thread
2.关于锁的释放
wait - > 释放锁
sleep - > 不释放锁
3.使用范围
wait - > 在同步代码块中使用
sleep - > 任何地方
4.是否捕获异常
wait - > 不用捕获异常
sleep - > 必须捕获异常
3、Lock锁
传统 Synchronized
/**
* 公司中的开发
* 线程是一个单独的资源类,没有任何附属操作
* 1.属性 2.方法
*/
public class Demo1 {
public static void main(String[] args) {
// 并发,多线程操作操作同一个资源,把资源类丢入线程
Ticket ticket = new Ticket();
// @FunctionLInterface 函数式接口,jdk 1.8 lambda 表达式 ()->{}
new Thread(()->{
for (int i = 1; i < 30; i ++) {
ticket.sale();
}
}, "A").start();
new Thread(()->{
for (int i = 1; i < 30; i ++) {
ticket.sale();
}
}, "B").start();
new Thread(()->{
for (int i = 1; i < 30; i ++) {
ticket.sale();
}
}, "C").start();
}
}
class Ticket {
private int number = 20;
public synchronized void sale () {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了" + number-- + "票,剩余" + number);
}
}
}
Lock 接口
- ReentrantLock
public ReentrantLock() {
// 非公平锁
sync = new NonfairSync();
}
/**
* Creates an instance of {@code ReentrantLock} with the
* given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() /*公平锁*/ : new NonfairSync();
}
java 默认非公平锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Demo2 {
public static void main(String[] args) {
// 并发,多线程操作操作同一个资源,把资源类丢入线程
Ticket2 ticket = new Ticket2();
// @FunctionLInterface 函数式接口,jdk 1.8 lambda 表达式 ()->{}
new Thread(()->{ for (int i = 1; i < 30; i ++) ticket.sale(); }, "A").start();
new Thread(()->{ for (int i = 1; i < 30; i ++) ticket.sale(); }, "B").start();
new Thread(()->{ for (int i = 1; i < 30; i ++) ticket.sale(); }, "C").start();
}
}
// Lock
// 1. new ReentrantLock();
// 2. Lock.lock();
// 3. lock.unlock();
class Ticket2 {
private int number = 20;
Lock lock = new ReentrantLock();
public void sale () {
lock.lock(); // 加锁
try {
// 业务代码
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了" + number-- + "票,剩余" + number);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 解锁
lock.unlock();
}
}
}
Synchronized 和 Lock 的区别
- Synchronized 内置关键字,Lock 是一个 Java 类
- Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到锁
- Synchronized 会自动释放锁,Lock 需要手动释放锁
- Synchronized 一个线程获得锁阻塞,Lock锁不一定等待下去
- Synchronized 可重入锁,不可中断,非公平锁;Lock 可重入锁,可以判断锁,可设置是否公平,默认非公平。
- Synchronized 适合少量代码同步,Lock 适合锁大量同步代码
4、生产者和消费者问题
Synchronized 版
package pc;
/**
* 线程之间通信问题,生产者和消费者问题,等待通知,等待唤醒
* 线程交替执行 A B 操作同一个变量,num = 0
* A num + 1
* B num - 1
*/
public class Test1 {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i ++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i ++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
}
}
// 判断等待 业务 通知
class Data { // 资源类
private int number = 0;
public synchronized void increment() throws InterruptedException {
while (number != 0) {
// 等待
this.wait();
}
number ++;
System.out.println(Thread.currentThread().getName() + ">>>" + number);
// 通知其他线程,执行完毕
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while (number == 0) {
// 等待
this.wait();
}
number --;
System.out.println(Thread.currentThread().getName() + ">>>" + number);
// 通知其他线程,执行完毕
this.notifyAll();
}
}
JUC 版生产者和消费者
package pc;
import lombok.var;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test2 {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
data.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "D").start();
}
}
// 判断等待 业务 通知
class Data2 { // 资源类
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// condition.await(); // 等待
// condition.signalAll(); // 唤醒全部
public void increment() throws InterruptedException {
try {
lock.lock();
while (number != 0) {
// 等待
condition.await();
}
number++;
System.out.println(Thread.currentThread().getName() + ">>>" + number);
// 通知其他线程,执行完毕
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
try {
lock.lock();
while (number == 0) {
// 等待
condition.await();
}
number--;
System.out.println(Thread.currentThread().getName() + ">>>" + number);
// 通知其他线程,执行完毕
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Condition 精准的通知和唤醒线程
package pc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* A 执行完调用 B
* B 执行完调用 C
*/
public class Test3 {
public static void main(String[] args) {
Data3 data3 = new Data3();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data3.printA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data3.printB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data3.printC();
}
}, "C").start();
}
}
class Data3 {
private Lock lock = new ReentrantLock();
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
private int number = 1; // 1A 2B 3C
public void printA(){
try {
lock.lock();
// 业务代码 判断 - > 执行 - > 通知
while (number != 1) {
condition1.await();
}
System.out.println(Thread.currentThread().getName() + " >>> AAA");
// 唤醒 B
number = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
try {
lock.lock();
while (number != 2) {
condition2.await();
}
// 业务代码 判断 - > 执行 - > 通知
System.out.println(Thread.currentThread().getName() + " >>> BBB");
number = 3;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
try {
lock.lock();
while (number != 3) {
condition3.await();
}
System.out.println(Thread.currentThread().getName() + " >>> CCC");
// 业务代码 判断 - > 执行 - > 通知
number = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
5、8锁现象
深刻理解锁
package lock8;
import java.util.concurrent.TimeUnit;
/**
* 锁的 8 个问题
* 1、标准情况下,两个线程先打印 call 还是 sendSms 1、sendSms 2、call
* 1、sendSms 延时4秒,两个线程先打印 call 还是 sendSms 1、sendSms 2、call
*/
public class Test1 {
public static void main(String[] args) {
Phone phone = new Phone();
new Thread(() -> {
phone.sendSms();
}, "A").start();
// 捕获
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
phone.call();
}, "B").start();
}
}
class Phone{
// Synchronized 锁定对象,谁先拿到谁执行
public synchronized void sendSms(){
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public synchronized void call(){
System.out.println("call");
}
}
package lock8;
import java.util.concurrent.TimeUnit;
/**
* 3、增加了一个普通方法后,先执行 sendSMS 还是 hello
* 4、两个对象,两个同步方法,先执行 sendSMS 还是 hello
*/
public class Test2 {
public static void main(String[] args) {
Phone2 phone1 = new Phone2();
Phone2 phone2 = new Phone2();
new Thread(() -> {
phone1.sendSms();
}, "A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone2 {
// Synchronized 锁定对象,谁先拿到谁执行
public synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public synchronized void call() {
System.out.println("call");
}
public void hello() {
System.out.println("hello");
}
}
package lock8;
import java.util.concurrent.TimeUnit;
/**
* 5、增加两个静态同步方法,只有一个对象,先 sendSMS 还是先 call
* 6、两个对象,增加两个惊天同步方法
*/
public class Test3 {
public static void main(String[] args) {
Phone3 phone1 = new Phone3();
Phone3 phone2 = new Phone3();
new Thread(() -> {
phone1.sendSms();
}, "A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone1.call();
}, "B").start();
}
}
class Phone3 {
// Synchronized 锁定对象,谁先拿到谁执行
// static 静态方法
// 类加载就有了,锁的是 class
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
public static synchronized void call() {
System.out.println("call");
}
public void hello() {
System.out.println("hello");
}
}
package lock8;
import java.util.concurrent.TimeUnit;
/**
* 7、1个静态的同步方法,一个普通同步方法,一个对象
* 8、1个静态同步方法,1个普通同步方法,两个对象
*/
public class Test4 {
public static void main(String[] args) {
Phone4 phone1 = new Phone4();
Phone4 phone2 = new Phone4();
new Thread(() -> {
phone1.sendSms();
}, "A").start();
// 捕获
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(() -> {
phone2.call();
}, "B").start();
}
}
class Phone4 {
// 静态同步方法 锁的是 Class 类
public static synchronized void sendSms() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("sendSms");
}
// 普通同步方法 锁的调用者
public synchronized void call() {
System.out.println("call");
}
public void hello() {
System.out.println("hello");
}
}
小结
new this 锁的是对象是方法的调用者,也就是 new 出来的对象,调用这个方法
static Class 的唯一一个模板
锁的对象相同,谁先获得锁先执行,锁的对象不同,分开执行不冲突
6、集合类不安全
List 类不安全
package unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* ConcurrentModificationException 并发修改异常
*/
public class ListTest {
public static void main(String[] args) {
// 并发下 ArrayList 不安全
/**
* ArrayList<String> list = new ArrayList<>();
* List<String> list = new Vector<>();
* List<String> list = Collections.synchronizedList(new ArrayList<>());
*/
// CopyOnWrite 写入时复制
// 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
// 写入的时候避免覆盖,造成数据分离
// CopyOnWriteArrayList 比 Vector 好在在哪里?
// CopyOnWriteArrayList 使用的 lock 锁,没有使用 Synchronized
List<String> list = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
list.add(UUID.randomUUID().toString().substring(0,3));
System.out.println(list);
}, String.valueOf(i)).start();
}
}
}
Set 不安全
package unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
public class SetTest {
public static void main(String[] args) {
// 1、Set<String> set = new HashSet<>();
// 2、Set<String> set = Collections.synchronizedSet(new HashSet<String>());
Set<String> set = new CopyOnWriteArraySet<String>();
for (int i = 0; i < 30; i++) {
new Thread(() -> {
set.add(UUID.randomUUID().toString().substring(0,3));
System.out.println(set);
}, String.valueOf(i)).start();
}
}
}
HashSet 的底层实现
public HashSet() {
map = new HashMap<>();
}
// add set 是无序的,map 的 key 无法重复
public boolean add(Ee) {
return map.put(e, PRESENT) == null;
}
map 不安全
package unsafe;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class MapTest {
public static void main(String[] args) {
// map 是这样用吗?
// 默认等价于什么 new HashMap<>();
// 负载因子,初始化容量
// Map<String, String> map = new HashMap<>();
// Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());
Map<String, String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,3));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
}
7、Callable
- 可以有返回值
- 可以抛出异常
- 方法不同,call()
package callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new Thread(new MyThread()).start();
// new Thread(new FutureTask<v>()).start();
// new Thread(new FutureTask<v>(Callable)).start();
MyThread myThread = new MyThread();
FutureTask futureTask = new FutureTask<>(myThread);
// 适配类
new Thread(futureTask, "A").start();
String string = (String) futureTask.get(); // get 可能等待,会阻塞
System.out.println(string);
}
}
class MyThread implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("call()");
return "123456";
}
}
8、常用辅助类
- CountDownLatch 减法计数器
package add;
import java.util.concurrent.CountDownLatch;
// 计数器
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 总数是 6,必须要执行任务时使用
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 5; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "Go out");
countDownLatch.countDown(); // 数量 -1
}, String.valueOf(i)).start();
}
countDownLatch.await(); // 等待计数器归零再向下执行
System.out.println("close the door");
}
}
countDownLatch.countDown();
// 数量 -1
countDownLatch.await();
// 等待计数器归零再向下执行
- CyclicBarrier 加法计数器
指定个数线程执行完再执行操作
package add;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,() -> {
System.out.println("成功");
});
for (int i = 0; i < 7; i++) {
final int temp = i;
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + temp);
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}, String.valueOf(i)).start();
}
}
}
- Semaphore 信号量
同一时间只能有指定数量得到线程
package add;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(6);
for (int i = 0; i < 6; i++) {
new Thread(() -> {
// acquire 得到
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "抢到");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "离开");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// release 释放
semaphore.release();
}
}, String.valueOf(i)).start();
}
}
}
semaphore.acquire();
获得,满了将等待
semaphore.release();
释放,当前信号量释放 +1,唤醒等待的线程
作用:多个共享资源互斥使用,并发限流。
9、读写锁
ReadWriteLock
package rw;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 独占锁(写锁),一次只能一个线程占有
* 共享锁(读锁),可以多个线程占有
* ReadWriteLock
* 读 - 读
* 读 - 写
* 写 - 写
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.put(temp+"", temp);
}, String.valueOf(i)).start();
}
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread(() -> {
myCache.get(temp+"");
}, String.valueOf(i)).start();
}
}
}
/**
* 自定义缓存
*/
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
// 读写锁,更加细粒度的控制
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 存,写入时同时被一个线程执行
public void put(String key, Object value) {
try {
readWriteLock.writeLock().lock();
System.out.println(Thread.currentThread().getName() + "写入key" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入完成" + key);
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
// 取,读
public void get(String key) {
try {
readWriteLock.readLock().lock();
System.out.println(Thread.currentThread().getName() + "获取key" + key);
map.get(key);
System.out.println(Thread.currentThread().getName() + "获取完成" + key);
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
10、阻塞队列
Queue
Dqueue:双端队列
BlockingQueue:阻塞队列
AbstractQueue:非阻塞队列
四组 API
方式 | 抛出异常 | 不会抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer | put | offer(e, timeout, unit) |
移除 | remove | pool | take | poll(timeout, unit) |
检测队首元素 | element | peek |
抛出异常
package blockqueue;
import java.util.Collection;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
public class BlcokQueueDemo {
public static void main(String[] args) throws InterruptedException {
// Collection
// List
// Set
// BlockingQueue
// test1();
// test2();
// test3();
test4();
}
/**
* 抛出异常
*/
public static void test1() {
// 参数队列大小
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.add("a"));
System.out.println(arrayBlockingQueue.add("b"));
System.out.println(arrayBlockingQueue.add("c"));
// 检测队首元素
System.out.println(arrayBlockingQueue.element());
// 队列满抛出异常
// Exception in thread "main" java.lang.IllegalStateException: Queue full
// System.out.println(arrayBlockingQueue.add("d"));
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
// 没有元素抛出异常
// Exception in thread "main" java.util.NoSuchElementException
System.out.println(arrayBlockingQueue.remove());
}
/**
* 有返回值,不抛出异常
*/
public static void test2() {
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
// 超出队列长度,返回 false 不抛出异常
// System.out.println(blockingQueue.offer("d"));
// 检测队首元素
System.out.println(blockingQueue.peek());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
// 返回 null,没有异常
System.out.println(blockingQueue.poll());
}
/**
* 等待,阻塞(一直阻塞)
*/
public static void test3() throws InterruptedException {
ArrayBlockingQueue blockingQUeue = new ArrayBlockingQueue<>(3);
// 一直阻塞
blockingQUeue.put("a");
blockingQUeue.put("b");
blockingQUeue.put("c");
// 超过队列长度一直阻塞
// blockingQUeue.put("d");
System.out.println(blockingQUeue.take());
System.out.println(blockingQUeue.take());
System.out.println(blockingQUeue.take());
// 没有元素会一直阻塞
System.out.println(blockingQUeue.take());
}
/**
* 等待,阻塞,超时退出
*/
public static void test4() throws InterruptedException {
ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
// 超时等待两秒就退出
System.out.println(blockingQueue.offer("d", 2, TimeUnit.SECONDS));
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll());
System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
}
}
SynchronousQueue 同步队列
没有容量,put 元素之后需要 take 取出来。
package blockqueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* 同步队列
* SynchrnousQueue 不存储元素
* put 了一个元素,必选先从里面取出来,否则不能再put进去东西
*/
@SuppressWarnings("all")
public class SynchronizedDemo {
public static void main(String[] args) {
SynchronousQueue<String> synchronousQueue = new SynchronousQueue<>();
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + "put1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName() + "put2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName() + "put3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1").start();
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName());
synchronousQueue.take();
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName());
synchronousQueue.take();
System.out.println(Thread.currentThread().getName());
TimeUnit.SECONDS.sleep(2);
synchronousQueue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t2").start();
}
}
11、线程池
池化技术
程序的运行,会消耗系统资源,我们需要优化资源的使用,使用了池化技术
线程池,连接池,内存池,常量池,内存池等。
线程池的好处
- 降低资源消耗,创建和销毁线程非常消耗资源
- 提高响应速度
- 方便对线程的管理
方法
- Executors.newSingleThreadExecutor(); 单线程池
- Executors.newFixedThreadExecutor(); 定长线程池
- Executors.newCachedThreadExecutor(); 缓存线程池,可变长度线程池
package pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Executors 工具类三大方法
* 使用了线程池后,使用线程池创建线程
*/
public class Demo1 {
public static void main(String[] args) {
// 单个线程
// ExecutorService executor = Executors.newSingleThreadExecutor();
// 固定线程池
// ExecutorService executor = Executors.newFixedThreadPool(3);
// 可变长度线程池
ExecutorService executor = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 100; i++) {
// 使用线程池创建线程
executor.execute(() -> {
System.out.println(Thread.currentThread().getName());
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 线程池使用完毕关闭线程池
executor.shutdown();
}
}
}
参数
源码:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
// 本质:ThreadPoolExecutor ()
public ThreadPoolExecutor(int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程池大小
long keepAliveTime, // 超时时间
TimeUnit unit, // 超时单位
BlockingQueue<Runnable> workQueue, // 阻塞队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler) { // 拒绝策略
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
四种拒绝策略
AbortPolicy:默认拒绝策略,队列满了抛出异常
CallerRunsPolicy:队列满了不抛出异常,主线程进行处理
DiscardPolicy:不会抛出异常,放弃任务执行
DiscardOldesPolicy:队列满了,不抛出异常,尝试和最早的进行竞争
线程池大小如何设置
1、CPU 密集型,几核就是几
2、IO 密集型,> 程序中非常耗费 IO 的线程
12、四大函数式接口
函数式接口:只有一个方法的接口
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
function 函数式接口
package function;
import java.util.function.Function;
/**
* Function 函数型接口,有一个输入参数,一个输出参数
* 函数型接口可以用 lambda 表达式简化
*/
public class Demo1 {
public static void main(String[] args) {
Function function = new Function<String, String>() {
@Override
public String apply(String o) {
return null;
}
};
Function function1 = (str) -> {return str};
System.out.println(function.apply("asdf"));
}
}
断定型接口
package function;
import java.util.function.Predicate;
/**
* 断定型接口
*/
public class Demo2 {
public static void main(String[] args) {
Predicate<String> predicate = new Predicate<String>() {
@Override
public boolean test(String s) {
return s.isEmpty();
}
};
Predicate<String> predicate1 = (str) -> {return str.isEmpty();};
System.out.println(predicate1.test(""));
}
}
Consumer 消费型接口
package function;
import java.util.function.Consumer;
/**
* Consumer 消费型接口,只有输入,没有返回值
*/
public class Demo3 {
public static void main(String[] args) {
Consumer<String> stringConsumer = new Consumer<String>() {
@Override
public void accept(String o) {
System.out.println(o);
}
};
Consumer<String> consumer = (str) -> {
System.out.println(str);
};
stringConsumer.accept("ooooo");
}
}
Supplier 供给型接口
package function;
import java.util.function.Supplier;
/**
* 供给型接口,没有参数,有返回值
*/
public class Demo4 {
public static void main(String[] args) {
Supplier<String> stringSupplier = new Supplier<String>() {
@Override
public String get() {
return "方法调用了";
}
};
Supplier supplier = () -> {return "asdf";};
System.out.println(supplier.get());
}
}
13、Stream 流式计算
14、ForkJoin
ForkJoin 特点:工作窃取。ForkJoin 中维护的都是双端队列
15、异步回调
16、JMM
Volatitle 是 Java 虚拟机提供的轻量级的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
JMM:java 内存模型
JMM 一些同步的约定
- 线程解锁前,必须把共享变量立刻刷回主存
- 线程加锁前,必须读取主存中的最新值到工作内存中
- 加锁和解锁是同一把锁
线程:工作内存、主内存
内存交互的八种操作
lock 锁定 => unlock 解锁 => read 读取 => load 载入 => use 使用 => assign 赋值 => store 存储 => write 写入
17、Volatitle
- 保证可见性
public class JMMDemo {
// 使用 volatile 保证可见性
private volatile static int num = 0;
public static void main(String[] args) {
// 线程 1
new Thread(() -> {
while (num == 0) {
}
}).start();
try{
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
num = 1;
}
}
- 不保证原子性
public class VDemo2 {
// volatile 不保证原子性
private volatile static int num = 0;
public void add(){
num ++;
}
public static void main(String[] args) {
// 理论上应该为 20000
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for(int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) { // main GC
Thread.yield();
}
}
}
如果不使用 lock 和 Synchronized 怎么保证原子性
使用原子类进行操作
public class VDemo2 {
// 原子类的 Integer
private static AtomicInteger num = new AtomicInteger();
public void add(){
// AtomicInteger +1 方法,CAS
num.getAndIncrement();
}
public static void main(String[] args) {
// 理论上应该为 20000
for (int i = 1; i <= 20; i++) {
new Thread(() -> {
for(int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while (Thread.activeCount() > 2) { // main GC
Thread.yield();
}
}
}
这些类底层与操作系统挂钩,在内存中修改值,unsafe 类是个特殊的存在
指令重排
源代码 => 编译器优化重排 => 指令并行可能进行重排 => 内存系统也会进行重排 => 执行
处理器作指令重排时,考虑:数据之间的依赖性问题
volatile 可以禁止指令重排:内存屏障。CPU指令。作用:
- 保证特定的执行顺序
- 可以保证某些变量的内存可见性(利用这些特性可以保证 volatile 的可见性)
Volatile 在单例模式中使用的多
18、单例模式
饿汉式
// 饿汉式单例
public class Hungry {
// 可能会浪费空间
private byte[] data = new byte[1024*1024];
private Hungry(){};
private final static Hungry HUNGRY = new Hungry();
public static Hungry getInstance(){
return HUNGRY;
}
}
懒汉式
public class lazyMan {
private LazyMan(){};
private volatile static LazyMan lazyMan;
// 双重检测锁模式,DCL 懒汉式
public static LazyMan getInstance(){
if (lazyMa == null ) {
synchronized (LazyMan.class){
if(LazyMan == null) {
// 1、分配内存空间
// 2、执行构造方法,初始化对象
// 3、把对象指向内存空间
lazyMan = new LazyMan(); // 不是原子性操作
}
}
}
return lazyMan;
}
}