JUC
java.util.concurrent
子类介绍
java.util.concurrent.atomic
原子性
java.util.concurrent.locks
lock锁
在业务中
普通的线程代码:Thread
Runnable: 没有返回值,效率相对于Callable低。
线程和进程
对于Java而言的线程:Thread、Runnable、Callable。
并发和并行
并发(多线程操作同一个资源)
- CPU一核,模拟出多条线程。天下武功,唯快不破,快速交替。
并行(多个人一起行走)
线程有几个状态
六个。
1 2 3 4 5 6
| NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED;
|
wait/sleep区别
来自不同的类
wait -> Object
sleep -> Thread
关于锁的释放
wait 会释放锁
sleep 不会释放锁
使用的范围不同
wait 必须在同步代码块中
sleep 可以在任何地方使用
是否需要捕获异常
wait 不需要捕获异常
sleep 必须要捕获异常
Lock锁
synchronized
Lock接口以及其实现类
![image]()
Lock是一个接口,有以下实现类。
ReentrantLock 可重用锁
ReadLock 读锁
WriteLock 写锁
公平锁 非公平锁
![image]()
公平锁:先来后到。
非公平锁:可以插队。(默认)
synchronized 和 Lock 区别
synchronized 内置的Java关键字。 Lock是一个Java类。
synchronized 无法判断获取锁的状态。Lock可以判断是否获取到了锁。
synchronized 会自动释放锁。Lock必须要手动释放锁,若不释放,则会死锁。
synchronized 线程1(获得锁,阻塞),线程2(等待)(悲)。Lock锁不一定会一直等待,可以尝试获取锁。
synchronized 可重入锁,不可中断的,非公平;Lock,可重入锁,可以判断锁,非公平(可以自己设置)。
synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码。
生产者和消费者问题
synchronized版本
代码:(错误的版本,正确的版本下面)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
public class Demo3_生产者和消费者 { 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.notifyAll(); } public synchronized void decrement() throws InterruptedException { if(number==0){ this.wait(); } number--; System.out.println(Thread.currentThread().getName()+" -> "+number); this.notifyAll(); }
}
|
出现问题:两个线程正常运行 四个线程出现问题。 虚假唤醒。
当出现超过两个线程,就会出现虚假唤醒。
当一个生产者生产结束后,会唤醒其他所有消费者,导致消费者全部开始消费,若消费者多于1个,会出现多次消费,导致错误。
为解决这个问题,可以将if换成while,来防止虚假唤醒。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
public class Demo3_生产者和消费者 { 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版生产者和消费者问题
Lock用来替代synchronized的名词
![image]()
代码实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
| public class Demo3_生产者和消费者JUC版 { public static void main(String[] args) { Data2 data = new Data2(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.increment(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.increment(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.decrement(); } },"C").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.decrement(); } },"D").start();
} }
class Data2{ private int number = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition();
public void increment() { lock.lock(); try { while (number!=0){ condition.await(); } number++; condition.signalAll(); System.out.println(Thread.currentThread().getName()+" => "+number); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void decrement() { lock.lock(); try { while (number==0){ condition.await(); } number--; condition.signalAll(); System.out.println(Thread.currentThread().getName()+" => "+number); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
|
Condition精准通知唤醒
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
|
public class Demo4_通过生产者消费者问题学习Condition精准通知 { public static void main(String[] args) { Data3 data = new Data3();
new Thread(()->{ for (int i = 0; i < 10; i++) { data.printA(); } },"A").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.printB(); } },"B").start(); new Thread(()->{ for (int i = 0; i < 10; i++) { data.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; public void printA(){ lock.lock(); try { while (number!=1){ condition1.await(); } System.out.println(Thread.currentThread().getName()+" A"); number = 2; condition2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB(){ lock.lock(); try { while (number!=2){ condition2.await(); }
System.out.println(Thread.currentThread().getName()+" B"); number = 3; condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC(){ lock.lock(); try { while (number != 3) { condition3.await(); } System.out.println(Thread.currentThread().getName()+" C"); number = 1; condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
|
其他
1 2 3
|
System.out.println(Runtime.getRuntime().availableProcessors());
|
一些问题
Q:Java默认有几个线程?
A:2个。main和GC。
Q: Java可以开启线程吗?
A:不可以。通过分析new Thread().start(),可以得到一个native方法private native void start0();
,这个方法是个本地方法,调用的是底层的C++。Java不能直接操作硬件。