1.JUC简介
JUC其实是java.util 工具包
2.线程和进程
进程:一个程序,可以包含多个线程。
Java默认有2个线程:mian、GC。
Java开启线程的三个办法:Thread、Runnable、Callable。
Java实际开不了线程,因为Java无法操作硬件,通过调用底层c++的本地方法start0创建线程。
-
并发、并行
并发:多线程操作同一个资源,一核 CPU 模拟出来多条线程;本质:充分利用CPU的资源;
并行:CPU 多核 ,多个线程可以同时执行; -
线程的状态
public enum State { /** * 新生 */ NEW, /** * 运行 */ RUNNABLE, /** * 阻塞 */ BLOCKED, /** * 等待 */ WAITING, /** * 超时等待 */ TIMED_WAITING, /** * 终止 */ TERMINATED; }
-
wait/sleep 区别
- 来自不同的类
wait => Object
sleep => Thread - 锁的释放
wait 会释放锁,sleep 不会释放 - 使用范围不同
wait必须在同步代码块中,sleep可以在任何地方 - 是否需要捕获异常(线程都会有中断异常)
wait 不需要捕获异常
sleep 必须要捕获异常
注:如果需要用sleep,用TimeUtil
TimeUnit.SECONDS.sleep(1);//睡一秒 TimeUnit.DAYS.sleep(1);//睡一天
- 来自不同的类
3. Synchronized和Lock
-
Synchronized
-
Lock
根据源码可以看出ReentrantLock可以传参,true代表公平锁,false代表非公平锁
公平锁:先来后到
非公平锁:可以插队 (默认) -
Synchronized 和 Lock 区别
- Synchronized 内置的Java关键字, Lock 是一个Java类
- Synchronized 无法判断获取锁的状态,Lock 可以判断是否获取到了锁
- Synchronized 会自动释放锁,lock 必须要手动释放锁!如果不释放锁,死锁
- Synchronized 线程 1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去
- Synchronized 可重入锁,不可以中断的,非公平;Lock ,可重入锁,可以 判断锁,非公平(可以
自己设置) - Synchronized 适合锁少量的代码同步问题,Lock 适合锁大量的同步代码
4. 生产者和消费者问题
-
Synchronized 版
/** * 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒 * 线程交替执行 A B 操作同一个变量 num = 0 * A num+1 * B num-1 */ public class Test { public static void main(String[] args) { Cal cal = new Cal(); new Thread(()->{ for(int i=0;i<5;i++){ try { cal.incr(); } catch (InterruptedException e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for(int i=0;i<5;i++){ try { cal.decr(); } catch (InterruptedException e) { e.printStackTrace(); } } },"B").start(); } } class Cal{ private Integer num = 0; // +1 public synchronized void incr() throws InterruptedException { if(num!=0){ this.wait(); } num++; System.out.println(Thread.currentThread().getName()+"=>"+num); this.notifyAll(); } public synchronized void decr() throws InterruptedException { if(num==0){ this.wait(); } num--; System.out.println(Thread.currentThread().getName()+"=>"+num); this.notifyAll(); } }
输出:
A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0 A=>1 B=>0
看似以上代码没错,但线程多的情况下会出现 虚假唤醒 现象
将if 改为 while即可
-
JUC版的生产者和消费者
public class Test03 { public static void main(String[] args) { Cal2 cal = new Cal2(); new Thread(()->{ for(int i=0;i<50;i++){ try { cal.incr(); } catch (Exception e) { e.printStackTrace(); } } },"A").start(); new Thread(()->{ for(int i=0;i<50;i++){ try { cal.decr(); } catch (Exception e) { e.printStackTrace(); } } },"B").start(); new Thread(()->{ for(int i=0;i<50;i++){ try { cal.incr(); } catch (Exception e) { e.printStackTrace(); } } },"C").start(); new Thread(()->{ for(int i=0;i<50;i++){ try { cal.decr(); } catch (Exception e) { e.printStackTrace(); } } },"D").start(); } } class Cal2{ private Integer num = 0; Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); // +1 public void incr() { lock.lock(); try { if(num!=0){ condition.await(); } num++; System.out.println(Thread.currentThread().getName()+"=>"+num); condition.signal(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } public void decr() { lock.lock(); try { if(num==0){ condition.await(); } num--; System.out.println(Thread.currentThread().getName()+"=>"+num); condition.signal(); } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } } }
输出:
A=>1 D=>0 A=>1 D=>0 C=>1 B=>0 C=>1 B=>0
线程输出顺序有些不对,可以通过new多个Condition,唤醒的时候唤醒指定Condition即可