juc并发编程
java.util.concurrent
java.util.atomic
java.util.concurrent.locks
Runable 没有返回值,效率相比Callable较低
线程与进程
一个进程往往包含多个线程
java默认两个线程:main GC
java不可以开启线程,调用的本地方法(native),调用底层c++
并发与并行
并发:多线程操作同一资源
-
cpu一核,模拟多个线程,快速交替执行
并行:多个线程同时执行
-
cpu多核,多个线程同时执行:线程池
线程6个状态
-
新生
-
运行
-
阻塞
-
等待,死死地等
-
超时等待
-
终止
wait/sleep区别
来自不同的类
-
wait:Object
-
sleep:Thread
锁的释放
-
wait:会释放锁
-
sleep:不会释放锁
使用范围
-
wait:同步代码块
-
sleep:任何地方
是否需要捕获异常
-
wait:不需要捕获异常
-
sleep:需要捕获异常
Lock锁
传统Synchronized
本质:队列,锁
Lock接口
实现类:
-
可重入锁(ReentrantLock)(常用)
-
默认非公平锁,可以通过增加构造参数实现公平锁
-
-
读锁(ReentrantReadWriteLock.ReadLock)
-
写锁(ReentrantReadWriteLock.WriteLock)
公平锁:先来后到
非公平锁:可以插队(默认)
线程就是一个单独的资源类
并发:多线程操作同一个资源类,把资源类丢进线程
@Functionalinterface
函数式接口,jdk1.8 lambda表达式
(参数)->{代码}
Lock三部曲
-
new ReentrantLock();
-
lock.lock();//加锁
-
finally => lock.unlock;//解锁
Synchronzied和Lock区别
-
Synchronzied内置的java关键字,Lock一个java类
-
Synchronzied无法判断获得锁的状态,Lock可以判断是否获得了锁
-
Synchronzied会自动释放锁,Lock必须手动释放锁,如果不释放就死锁
-
Synchronzied线程1(获得锁,阻塞),线程2(等待,傻傻的等),Lock锁不一定会一直等待下去
-
Synchronzied可重入锁,不可以中断,非公平,Lock可重入锁,可以判断锁,非公平(可以设置公平)
-
Synchronzied适合少量代码的同步问题,Lock适合大量代码的同步问题
生产者消费者问题
Synchronzied
Synchronzied修饰方法,使用wait()函数进行等待,使用notifyAll()函数通知其他进程
等待的代码块要用while进行循环判断,而不是使用if,防止虚假唤醒问题
juc
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
Condition conditionA = lock.newCondition();
lock.lock();
condition.await(); // 等待
condition.signalAll(); // 唤醒全部
conditionA.signal();//唤醒指定
finally => lock.unlock();
锁的对象
普通同步方法:锁的是调用者(当前资源类对象 new)
静态同步方法:锁的是Class模板类(static)
集合类不安全
list不安全
java.util.ConcurrentModificationException 并发修改异常
并发下 ArrayList 不安全(Synchronized) 解决方案;
-
1、List<String> list = new Vector<>();
-
2、List<String> list = Collections.synchronizedList(new ArrayList<> ());
-
3、List<String> list = new CopyOnWriteArrayList<>();
CopyOnWrite 写入时复制 COW 计算机程序设计领域的一种优化策略; 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖) 在写入的时候避免覆盖,造成数据问题! 读写分离
Set不安全
-
Set<String> set = Collections.synchronizedSet(new HashSet<>());
-
Set<String> set = new CopyOnWriteArraySet<>();
Map 不安全
-
Map<String, String> map = new ConcurrentHashMap<>();
Callable
与runable相比
1、可以有返回值
2、可以抛出异常
3、方法不同,run()/ call()
FutureTask(runable的一个实现类)
new Thread(new FutureTask<V>( Callable )).start();
class MyThread implements Callable<V> {@Override call();}
细节: 1、有缓存(效率高) 2、结果可能需要等待,会阻塞!
辅助类
CountDownLatch减法计数器
countDownLatch.countDown(); // 数量-1
countDownLatch.await(); // 等待计数器归零,然后再向下执行
每次有线程调用 countDown() 数量-1,假设计数器变为0,countDownLatch.await() 就会被唤醒,继续执行!
CyclicBarrier加法计数器
CyclicBarrier cyclicBarrier = new CyclicBarrier(终值,lambda表达式(当到达终值时要执行,类似于回调函数));
cyclicBarrier.await(); // 等待
Semaphore信号量
Semaphore semaphore = new Semaphore(资源数量);
semaphore.acquire();// acquire() 得到
finally => semaphore.release(); // release() 释放
semaphore.acquire() 获得,假设如果已经满了,等待,等待被释放为止!
semaphore.release(); 释放,会将当前的信号量释放 + 1,然后唤醒等待的线程!
作用: 多个共享资源互斥的使用!并发限流,控制最大的线程数!
读写锁
读可以多线程同时读
写只可以一个线程写
-
独占锁(写锁) 一次只能被一个线程占有
-
共享锁(读锁) 多个线程可以同时占有
-
ReadWriteLock
-
读-读 可以共存!
-
读-写 不能共存!
-
写-写 不能共存!
// 读写锁: 更加细粒度的控制
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock lock = new ReentrantLock();
// 存,写入的时候,只希望同时只有一个线程写
readWriteLock.writeLock().lock();
finally => readWriteLock.writeLock().unlock();
// 取,读,所有人都可以读!
readWriteLock.readLock().lock();
finally => readWriteLock.readLock().unlock();