JUC
JUC是java.util.concurrent包的简称,在Java5添加,目的就是为了更好的支持高并发任务,让开发者进行多线程编程时减少竞争条件和死锁的问题,掌握JUC才能在高并发的场景拥有更多的处理方法
进程和线程
运行中的每一个程序就是一个进程,Java默认有两个线程(main,GC)
Java真的能开启线程吗
查看线程启动方法的源码发现,调用了一个native
方法,也就是说,Java是开不了的,调用了C++语言方法(Java无法直接操作系统)
并发和并行
- 并发:多个线程同时操作同一个资源
- 并行:在多核CPU下多个线程可以同时执行(单核CPU是模拟的)
线程状态
public enum State {
// 新建
NEW,
// 就绪
RUNNABLE,
// 阻塞
BLOCKED,
// 等待(一直等)
WAITING,
// 超时等待(限时等)
TIMED_WAITING,
// 终止
TERMINATED;
}
wait和sleep区别
- 来自不同的类
- wait来自Object
- sleep来自Thread
- 关于锁的释放:wait会释放锁,sheep不会释放
- 使用范围不同:wait只能在同步代码块中使用,sheep可以在任何地方使用
- 捕获异常问题:wait不用捕获异常,sheep需要
// 正常的睡眠方式,使用工具类
// 睡1天
TimeUnit.DAYS.sleep(1);
// 睡3秒
TimeUnit.SECONDS.sleep(3);
管程(Monitor)
- 管程在Java中是管理类的状态变量,让这个类是线程安全的
- 管程提供了对共享资源的互斥访问和同步机制
- 每个对象都有一个与之相关联的管程,可以使用
synchronized
关键字来实现对管程的加锁和解锁操作
Synchronized和Lock的区别
- synchronized是关键字,Lock是一个Java类
- synchronized无法获得锁的状态,Lock可以判断是否获取到了锁
- synchronized会自动释放锁,Lock需要手动释放锁
- synchronized为可重入锁,不可以中断,非公平;Lock默认也为可重入锁,可以判断锁,非公平(可设置)
- synchronized适合少量的代码,Lock适合大量的同步代码
生产者消费者
传统方式
/**
* 生产者消费者问题
*/
public class ProAndCon {
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 {
// 已有资源等待消费者消费后生产
if (number != 0) {
this.wait();
}
// 生产
number++;
System.out.println(Thread.currentThread().getName() + "-->" + number);
// 通知消费者开始消费
this.notify();
}
/**
* 消费
*/
public synchronized void decrement() throws InterruptedException {
// 没有资源等待生产
if (number == 0) {
this.wait();
}
// 消费
number--;
System.out.println(Thread.currentThread().getName() + "-->" + number);
// 通知生产者开始生产
this.notify();
}
}
注意:这里会出现问题,当两个以上线程运行时,就会出现虚假唤醒问题,这是由于if
判断导致,解决办法为改为where
判断
Lock实现
/**
* 生产者消费者问题
*/
public class ProAndCon {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.increment();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.decrement();
}
}, "B").start();
}
}
/**
* 资源
*/
class Data {
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
/**
* 生产
*/
public void increment() {
// 加锁
lock.lock();
try {
// 已有资源等待消费者消费后生产
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() {
// 加锁
lock.lock();
try {
// 没有资源等待生产
if (number == 0) {
condition.await();
}
// 消费
number--;
System.out.println(Thread.currentThread().getName() + "-->" + number);
// 通知生产者开始生产
// 通知其他线程
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
}
}
}
Condition精准唤醒
Condition的优势在于可以精准唤醒
/**
* 生产者消费者问题
*/
public class ProAndCon {
public static void main(String[] args) {
Data data = new Data();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.runA();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.runB();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 10; i++) {
data.runC();
}
}, "C").start();
}
}
/**
* 资源
*/
class Data {
// 标志位:1A,2B,3C
private int number = 1;
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
public void runA() {
// 加锁
lock.lock();
try {
// 不该自己执行就等待
while (number != 1) {
condition1.await();
}
// 执行完切换标志位
number = 2;
System.out.println(Thread.currentThread().getName());
// 唤醒自定线程
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
}
}
public void runB() {
// 加锁
lock.lock();
try {
// 不该自己执行就等待
while (number != 2) {
condition2.await();
}
// 执行完切换标志位
number = 3;
System.out.println(Thread.currentThread().getName());
// 唤醒自定线程
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
}
}
public void runC() {
// 加锁
lock.lock();
try {
// 不该自己执行就等待
while (number != 3) {
condition3.await();
}
// 执行完切换标志位
number = 1;
System.out.println(Thread.currentThread().getName());
// 唤醒自定线程
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 释放锁
lock.unlock();
}
}
}
能实现类似生产线的线程