一.什么是JUC
JUC是java.util.concurrent的缩写。一共有三个相关的jar包。高并发编程工具包。
并发编程本质:充分利用CPU资源。
补充:
- java不能开启线程,start()实际上是调用本地方法start0(),底层的c++方法去开启线程。
- wait与sleep区别:1.wait属于Object类,sleep属于Thread类。2.sleep不释放锁,wait释放锁。3.wait依赖于synchronized。4.wait需要被唤醒,sleep不需要。
二.Lock锁
- Lock是一个接口,有三个实现类:
- ReentrantLock (默认是非公平锁,可以插队;可以手动设置为公平锁)
- ReentrantReadWriteLock.ReadLock
- ReentrantReadWriteLock.WriteLock
- 对共享资源独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。
- 与synchronized区别:
-
- lock可以判断锁的状态,synchronized不能
- lock必须手动释放锁,synchronized自动释放锁
- synchronized与lock均可重入锁(指某个线程已经获得某个锁,可以再次获取锁而不会出现死锁) ,均为非公平锁。
- synchronized,一个线程获得锁,另一个线程会一直等待直到其释放锁;lock则不会,可通过lock.tryLock()尝试获取锁。
- 常用用法:
Lock lock = new ReentrantLock(); public void method(){ lock.lock(); try { //同步代码块 } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
三.生产者消费者问题
synchronized实现:
点击查看代码
public class ProCon {
public static void main(String[] args) {
Number number = new Number();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
number.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"a").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
number.sub();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"b").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
number.add();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"c").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
try {
number.sub();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"d").start();
}
}
/*
等待,业务处理,通知
*/
class Number{
private int num = 0;
public synchronized void add() throws InterruptedException {
while (num!=0){
this.wait();
}
num++;
// num等于0才加1,即num++最大只能为1
this.notifyAll();
System.out.println(Thread.currentThread().getName()+"->"+num);
}
public synchronized void sub() throws InterruptedException {
while (num==0){
this.wait();
}
num--;
this.notifyAll();
System.out.println(Thread.currentThread().getName()+"->"+num);
}
}
注:该方法中同步代码块中使用if判断等待存在虚假唤醒的可能性,使用while判断可规避该状况。
Lock锁实现(JUC):
Lock替换synchronized方法和语句的使用,Condition取代了对象监视器Obj。Condition提供与对象监视器类似的方法作用,即await(),signal(),signalAll()对应于wait(),notify(),notifyAll()。 一个Condition实例本质上绑定到一个锁。 要获得特定Condition实例的Condition实例,使用newCondition方法。点击查看代码
public class ProCon2 {
public static void main(String[] args) {
Number2 number2 = new Number2();
new Thread(()->{ for (int i = 0; i < 5; i++) { number2.add(); } },"a").start();
new Thread(()->{ for (int i = 0; i < 5; i++) { number2.sub(); } },"b").start();
new Thread(()->{ for (int i = 0; i < 5; i++) { number2.add(); } },"c").start();
new Thread(()->{ for (int i = 0; i < 5; i++) { number2.sub(); } },"d").start();
}
}
class Number2{
private int num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void add(){
lock.lock();
try {
while (num!=0){
condition.await();
}
num++;
condition.signalAll();
System.out.println(Thread.currentThread().getName()+"->"+num);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void sub(){
lock.lock();
try {
while (num==0){
condition.await();
}
num--;
condition.signalAll();
System.out.println(Thread.currentThread().getName()+"->"+num);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
Condition的优势:可以精准地唤醒线程
点击查看代码
public class ProCon3 {
public static void main(String[] args) {
ThreadTest test = new ThreadTest();
new Thread(()->{ for (int i = 0; i < 5; i++) { test.A(); } },"a").start();
new Thread(()->{ for (int i = 0; i < 5; i++) { test.B(); } },"b").start();
new Thread(()->{ for (int i = 0; i < 5; i++) { test.C(); } },"c").start();
}
}
class ThreadTest{
private int n = 1;
Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
public void A(){
lock.lock();
try {
while (n!=1){
conditionA.await();
}
n = 2;
// n为1执行业务,唤醒B
conditionB.signal();
System.out.println(Thread.currentThread().getName()+"->"+"is A");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void B(){
lock.lock();
try {
while (n!=2){
conditionB.await();
}
n = 3;
// n为2执行业务,唤醒C
conditionC.signal();
System.out.println(Thread.currentThread().getName()+"->"+"is B");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void C(){
lock.lock();
try {
while (n!=3){
conditionC.await();
}
n = 1;
// n为3执行业务,唤醒A
conditionA.signal();
System.out.println(Thread.currentThread().getName()+"->"+"is C");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}