进阶JUC
JUC是与属于三个包
java.util.concurrent
java.util.concurrent.atomic
java.util.concurrentlocks
回顾知识
-
线程和进程
进程:一个程序。例子:QQ.exe程序的集合。
线程:作为独立运行和独立调度的基本单位。例子:开一个进程Typora,写字,保存(线程负责)
补:
一个进程往往可以包含多个线程,至少包含一个!
线程就是一个单独的资源类,没有任何附属的操作!
Java默认有2个线程,是main/GC(垃圾回收)
java真的可以开启线程吗?开不了,它是调用本地方法库(即是底层的C++、native)来开启的,java无法直接操作硬件
-
并发、并行
并发:多线程操作同一个资源。(CPU一核,模拟出来多个线程,快速交替)
并行:多个线程同时执行。(CPU多核,多个线程同时执行;线程池)
补:
Runtime.getRuntime(),availableProcessors();//获取CPU的核数
并发编程:并发、并行
并发编程的本质:充分利用CPU的资源
-
线程有几个状态
NEW,新生
RUNNABLE,运行
BLOCKED,阻塞
WAITING,等待(死死的等)
TIMED_WAITING,超时等待
TERMINATED,终止
-
wait/sleep区别
-
来自不同的类
wait=>Object
sleep=>Thread
-
关于锁的释放
wait会释放锁,sleep不会释放锁(抱着锁睡觉了)
-
使用的范围是不同的
wait必须在同步代码块中
sleep可以在任何地方
-
是否需要捕获异常
wait不需要捕获异常
sleep必须需要捕获异常
-
-
Synchronized 和Lock区别
-
Synchronized 内置的Java关键字,Lock是一个Java类
-
Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
-
Synchronized 会自动释放锁,Lock必须要手动释放锁!如果不释放锁,死锁
-
Synchronized 线程1(获得锁,阻塞)、线程2(等待,傻傻的等);Lock锁就不一定会等待下去
-
Synchronized 可重入锁,不可以中断的,非公平;Lock,可重入锁,可以判断锁,分公平(可以自己设置)
注:
公平锁:十分公平–>可以先来后到
非公平锁:十分不公平–>可以插队(默认)
-
Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码!
-
生产者和消费者问题
面试的:单例模式、排序算法、生产者和消费者、死锁
线程之间的通信问题,生产者和消费者问题!等待唤醒,通知唤醒
线程交替执行 A B操作同一个变量 num = 0
A num + 1
B num - 1
等待应该总是出现在循环中(注意点:防止虚假唤醒–>线程也可以唤醒,而不被通知,中断或超时)
JUC版的生产者和消费者问题:
通过Lock找到Condition(Synchronized–wait–notify---->Lock–await–signal)
Condition 精准的通知和唤醒线程
代码实现:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class B{
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.increment();
} 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.increment();
} catch (InterruptedException e){
e.printStackTrace();
}
}
},"D").start();
}
}
//判断等待,业务,通知
class Data{
private int number = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment() throws InterruptedException{
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() throws InterruptedException{
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();
}
}
}
八锁现象理解锁
-
标准情况下,两个线程先打印,先执行是发短信还是打电话?1–>发短信 2–>打电话
-
发短信延迟4s下,两个线程先打印,先执行是发短信还是打电话?1–>发短信 2–>打电话
两个方法用的是同一个锁,谁先拿到谁执行!(Synchronized锁的对象是方法的调用者)
-
增加了一个普通方法后,先执行是发短信还是普通方法?1–>普通方法 2–>发短信
-
两个对象,两个同步方法,发短信延迟4s下,先执行是发短信还是打电话?1–>打电话 2–>发短信
-
增加两个静态的同步方法,只有一个对象,发短信延迟4s下,先执行是发短信还是打电话?1–>发短信 2–>打电话
-
两个对象,增加两个静态的同步方法,发短信延迟4s下,先执行是发短信还是打电话?1–>发短信 2–>打电话
两个对象的Class类模板只有一个,static静态方法,锁的是Class
-
一个静态的同步方法,一个普通的同步方法,一个对象,先执行是发短信还是打电话?1–>打电话 2–>发短信
-
一个静态的同步方法,一个普通的同步方法,两个对象,先执行是发短信还是打电话?1–>打电话 2–>发短信
集合类不安全
并发下ArrayList不安全吗?答:不安全(会报:ConcurrentModificationException 并发修改异常)
解决方案:
List list = new Vector<>();
List list = Collections.synchronizedList(new ArrayList<>());
List list = new CopyOnWriteArrayList<>());
CopyOnWrite写入时复制,COW计算机程序设计领域的一种优化策略;
多线程调用的时候,list读取的时候,在写入的时候避免覆盖,造成数据问题!
CopyOnWriteArrayList 比 Vector 厉害在哪里?
Vector 使用的是 Synchronized;CopyOnWriteArrayList 使用的是 Lock
并发下HashSet不安全吗?答:不安全
解决方案:
- Set set = Collections.synchronizedSet(new HashSet<>());
- Set set = new CopyOnWriteArraySet<>());
HashSet底层是什么?
HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持。它不保证set 的迭代顺序;特别是它不保证该顺序恒久不变,此类允许使用null元素。
在HashSet中,元素都存到HashMap键值对的Key上面,而Value时有一个统一的值private static final Object PRESENT = new Object();
,(定义一个虚拟的Object对象作为HashMap的value,将此对象定义为static final。)
map是这样子用的吗?:不是,工作中不用HashMap
默认等价于什么?new HashMap<>(16,0.75)(加载因子、初始化容量(1<<30))
HashMap呢?
解决方案:
- Map<String,String> set = Collections.synchronizedMap(new HashMap<>());
- Map<String,String> set = new ConcurrentHashMap<>());