Java中,JUC通常指的是Java并发工具包(Java Concurrency Utilities);下面总结juc相关概念;面试中基本上都会问到一些,实际工作中并发编程集合和锁都会用到。
1.上下文切换
概念:线程在进行交换的时候,线程的状态要保存或加载
2.synchronized
①特性:
a.原子性: 执行操作过程无法中断 (synchronized有原子性是与volatile最大区别)
b.可见性: 该线程资源状态对于其他线程可见;
c.有序性: 按照代码顺序执行
d.可重入性: 已锁对象可重复申请锁
②作用位置,锁对应对象
a.普通方法 synchronized加锁对象是this
b.静态方法 synchronized加锁对象是当前类的class
c.同步代码块 synchronized加锁对象是指定内容
③底层原理
获得锁计数器+1;释放锁计数器-1;底层指令是monitorenter和monitorexit
3.死锁
两个或多个线程被永久阻塞;(互相拿着对方的锁;)根本原因是交叉闭环申请;
4.守护线程
只要主线程结束 守护线程也跟着结束
5线程通信 wait notify/condition
线程之间不是独立个体,需要彼此之间通信写作(生产者和消费者问题)
①wait()--让当前线程释放对象锁进入阻塞;
②notify()--唤醒一个正在等待相应对象锁的进程;使其进入就绪队列(从等待到同步队列的过程);获取cpu执限制;
③condition和lock机制;更加面向对象灵活高效;多路通知特性
6.线程安全(集合)
①线程安全的List
CopyOnWriteArrayList; //效率不高Vector和Collections.synchronizedList
②线程安全的Set
CopyOnWriteArraySet;
③线程安全的Map 线程安全的
ConcurrentHashMap(推荐) //Hashtable效率较低
7.单例模式 双重检索
懒汉式、饿汉式、双重检索
//双重检索;第一次判断是否需要加锁;第二次判断是否需要创建对象;
public class Test02_Singleton3 {
private Test02_Singleton3(){}
private volatile static Test02_Singleton3 instance;
public static Test02_Singleton3 getInstance(){
if(instance==null){
synchronized (Test02_Singleton3.class){
if(instance==null){
instance=new Test02_Singleton3();
}
}
}
return instance;
}
}
8.重入锁ReentrantLock 自旋 读写锁 与synchronized区别
①区别
a: synchronized式关键字;TeentrantLock式类;
b: ReentrantLock灵活实现多路通知;
c:底层实现上ReentrantLock使用CAS实现(调用Unsafe和park方法);synchronized是monitor
②自旋锁:
自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。
③读写锁:ReentrantReadWriteLock
读读共享;读写互斥;写写互斥;(写操作占用锁)
使用于高并发场景;
9.阻塞队列
方法:异常,阻塞,null 原理
①概念:
当队列中为空时,从队列中获取元素的操作将被阻塞;
当队列满时,向队列中添加元素的操作将被阻塞
②常见方法
add/remove
往队列中添加元素 队列如果满了,添加元素会抛异常
如果队列没有满 添加一个元素会返回一个true
从队列头部移除数据 队列为空 抛异常(粗鲁)
offer/poll
如果队列满了 返回一个false
获取队列的首元素 如果拿不到 返回null
put/take
如果队列满的 插入动作会阻塞
如果队列元素为空 操作会阻塞
③常见队列:
ArrayBlockingQueue(数组实现定长)
LinkedBlockingQueue(链表实现变长)
SynchronousQueue:一个不存储元素的阻塞队列,每一个put操作必须等待take操作,否则不能添加元素,适用于传递数据的场景 在一个线程中使用数据传递给另外一个线程
④原理:
ArrayBlockingQueue的put和take使用的是同一把锁,(队列为满或者空的时候)put和take不能同时执行(而LinkedBlockingQueue的put和take使用的不是同一把锁,put和take能同时执行)
10.CAS
原理,问题ABA 缺点 与synchronized区别
①原理:
a.CAS有三个基本操作数 内存地址V,旧的预期值,要修改的值
b.当要去修改一个值的时候 会去进行一个比较,如果相同才进行置换
②ABA问题:
一个线程a将数值改成了b,接着又改成了a,此时CAS认为是没有变化,其实是已经变化过了,而这个问题的解决方案可以使用版本号version标识;在java5中AtomicStampedReference已解决问题。
11.线程池工作原理 参数 拒绝策略
“ThreadPoolExecutor”
①概念:
线程池就是提前创建若干个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务.
②工作原理:
a.创建线程池之后 活着的线程数量为0
b.当有新的任务来临的时候 做判断
当提交的任务<核心线程数 马上去创建对应的任务个核心线程数
当提交的任务>核心线程数 没有被处理的任务会交给等待队列去排队
如果等待队列满了 当提交的任务<(max线程数+队列大小) 创建非核心线程去处理任务
如果等待队列满了 当提交的任务>(max线程数+队列大小) 线程池会采用拒绝策略
③参数new ThreadPoolExecutor(核心线程数;最大线程数;存活时间;默认线程工厂;拒绝策略;)
④拒绝策略
a. AbortPolicy(默认)
抛出异常 比较粗鲁
b. CallerRunsPolicy
让线程池不能处理的任务交给调用线程池的线程来处理 比较有用的一个策略
能最大限度的处理我们的业务
c. DiscardOldestPolicy
抛弃等待最久的任务 关注的是新任务,时效性较好
d. DiscardPolicy
丢弃最新任务 关注的是老任务
12.线程池创建方法
①newFixedThreadPool
创建固定数量线程的线程池 适用于执行长期任务 性能好
②newSingleThreadExecutor
创建单个线程的线程池 任何时候都只有一个线程在执行 能保证任务的有序性
③newCachedThreadPool
创建可扩展的线程池 适用于短时异步任务
④newScheduleThreadPool
问题:前两个堆积请求会耗费非常大的内存,升值OOM,后一个可能创建很多线程;甚至OOM;
所以推荐使用ThreadPoolExecutor自定义参数创建;
13.Runnable和Callable
①是否有返回值
②.是否抛出异常
③落地方法不一样 run call
④Callable计算结果可以复用 调用过程结果没有出来会阻塞
14.JUC工具类
①CyclicBarrier
所有的资源到齐了才能做某件事情
②CountDownLatch
所有相关人员做了某些事情之后 某个人才能做某件事情
15.semaphore
Semaphore就是一个信号量,它的作用是限制某段代码块的并发数;目的限制资源访问(状态);
16.volatile
volatile修饰的变量对其他线程具备可见性 ( 保证工作内存修改的值会立即被更新到主存)
可以禁止指令重排
可以和CAS结合保证原子性.