JUC
1.什么是JUC
java.util.concurrent
2.进程和线程
java默认两个线程:main,gc
进程:一段程序的执行过程
线程:进程中一个单一顺序的控制流
java代码不能开启一个线程,通过native方法调用C++开启
并发:交替执行
并行:同时执行
//获取cpu核数
System.out.println(Runtime.getRuntime().availableProcessors());
并发编程的本质:充分利用CPU资源
java中定义的线程状态
- NEW :新生
- RUNNABLE :运行
- BLOCKED :阻塞
- WAITING :等待
- TIMED_WAITING :超时等待
- TERMINATED :终止
wait / sleep区别
- 来自不同类
wait --> Object
sleep --> Thread - 关于锁的释放
wait释放锁,sleep不释放 - 使用范围不同
wait在同步代码块中
sleep任何地方
3.Lock
synchronized 和 Lock区别
-
synchronized是java内置关键字,Lock是java类
-
synchronized无法判断获取锁的状态,Lock可判断是否获取了锁
-
synchronized会自动释放锁,Lock必须手动释放
-
synchronized 若线程1获得该锁并阻塞,线程2一直等待获取该锁;Lock不一定会一直等待
-
synchronized可重入锁,不可以中断,非公平;Lock可重入锁,非公平(可设置)
可重入锁:某个线程已经获取该锁,若再次获取该锁,可以获取,不会出现死锁
synchronized(this){
//第一次获取
synchronized(this){
//第二次获取,可获取,
}
}
-
synchronized适合锁少量的代码同步问题,Lock适合大量
4.生产者和消费者问题
synchronized (obj) {
while (<condition does not hold>)
obj.wait();
... // Perform action appropriate to condition
}
synchronized(obj){
while(<cond>)
obj.wait();
//...
obj.notifyAll();
}
/**Lock 等效代码*/
//成员变量
final Lock lock = new ReentrantLock();
final Condition condition = lock.newCondition();
//代码块
lock.lock();
try{
while(<cond>)
condition.await();
//...
condition.signalAll();
} finally{
lock.unlock();
}
Condition新技术
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//Data的printA printB printC依次执行
public class Test {
public static void main(String[] args) {
Data d = new Data();
new Thread(()->{
for (int i = 0; i < 5; i++) {
d.printA();
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
d.printB();
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 5; i++) {
d.printC();
}
},"C").start();
}
}
class Data{
private final Lock lock = new ReentrantLock();
//每个condition监视一个资源
private final Condition condition1 = lock.newCondition();
private final Condition condition2 = lock.newCondition();
private final Condition condition3 = lock.newCondition();
private int flag = 1; // 1.printA 2.printB 3.printC
public void printA(){
lock.lock();
try{
while(flag!=1)
condition1.await();
System.out.println(Thread.currentThread().getName()+"==>A");
//指定唤醒
flag = 2;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try{
while(flag!=2)
condition2.await();
System.out.println(Thread.currentThread().getName()+"==>B");
//指定唤醒
flag = 3;
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try{
while(flag!=3)
condition3.await();
System.out.println(Thread.currentThread().getName()+"==>C");
//指定唤醒
flag = 1;
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
5.八锁现象
锁是什么,如何判断锁的是谁
关于synchronized实现同步
synchronized 修饰方法:
普通成员方法,锁的是该类的对象
static方法,锁的是该类的Class对象
未被synchronized修饰的方法不需要获取锁对象
6.集合类不安全
List
//并发下ArrayList不安全
/**解决方案
*1. List list = new Vector();
*2. List list = Collections.synchronizedList(new ArrayList());
*3. List list = new CopyOnWriteArrayList();
*/
//CopyOnWrite 写时复制
Set
/**
*1. Set set = Collection.synchronizedSet(new HashSet());
*2. Set set = new CopyOnWriteArraySet();
*/
HashSet:
java HashSet 部分源码:
public HashSet() { map = new HashMap<>(); }
public boolean add(E e) { return map.put(e, PRESENT)==null; }
PRESENT是一个常量
map的key值本身要求是不重复的
Map
HashMap:
加载因子(DEFAULT_LOAD_FACTOR = 0.75)
初始化容量 (16)
/**
* HashMap map = new ConcurrentHashMap();
*/
7.Callable
- 可以有返回值
- 可以抛出异常
class A implements Callable<String>{ //<String> 泛型对应返回值类型
@Override
public String call() throws EXception{
//...
}
}
//使用Callable启动线程
FutureTask ft = new FutureTask(new A());
new Thread(ft).start();
String re = (String)ft.get(); //获取返回结果,可能会阻塞,等待结果
/**解释
*1. FutureTask 实现 RunnableFuture接口,RunnableFuture 继承 Runnable和Future接口
*2. FutureTask可传入Callable对象
*3. Thread传入Runnable对象,开启线程
/
以下代码输出:
call
YES
注意:只有一个 call 被打印
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class TestCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> f = new FutureTask<>(new A());
new Thread(f).start();
new Thread(f).start(); //结果会被缓存,提高效率
System.out.println(f.get());
}
}
class A implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("call");
return "YES";
}
}
可取消的异步计算。 该类提供了一个
Future
的基本实现 ,具有启动和取消计算的方法,查询计算是否完整,并检索计算结果。 结果只能在计算完成后才能检索; 如果计算尚未完成,则get
方法将阻止。 一旦计算完成,则无法重新启动或取消计算(除非使用runAndReset()
调用计算 )。
8.常用的辅助类
8.1 CountDownLatch
import java.util.concurrent.CountDownLatch;
public class TestCountDownLatch {
public static void main(String[] args) throws InterruptedException {
CountDownLatch c = new CountDownLatch(6); //减法计数器
for (int i = 0; i < 6; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName()+" executed");
c.countDown(); //减1
}).start();
}
c.await(); //等待计数器归零,然后向下执行
System.out.println("All executed");
}
}
8.2 CyclicBarrier
循环栅栏
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class TestCyclicBarrier {
public static void main(String[] args) {
CyclicBarrier c = new CyclicBarrier(7,()->{
System.out.println("YES");
}); //加法计数器,当c.await()有7次时,执行线程
for (int i = 0; i < 7; i++) {
final int ti = i;
new Thread(()->{
System.out.println(ti);
try {
c.await(); //等待
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
8.3 Semaphore
信号量
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class TestSemaphore {
public static void main(String[] args) {
Semaphore s = new Semaphore(3);
for (int i = 0; i < 7; i++) {
new Thread(()->{
//acquire 得到
try {
s.acquire();
System.out.println(Thread.currentThread().getName()+" in");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName()+" out");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//release 释放
s.release();
}
}).start();
}
}
}
/**
* 3个资源,
* acquire请求获取一个资源,如没有,阻塞,直到有可用的资源 (信号量-1)
* release释放资源 (信号量+1)
*/
9.读写锁
写与写之间互斥
读与写之间互斥
读与读之间不互斥
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class TestReadWriteLock {
public static void main(String[] args) {
Cache c = new Cache();
for (int i = 0; i < 5; i++) {
final int it = i;
new Thread(()->{
c.put(""+it,""+it);
},"w"+it).start();
}
for (int i = 0; i < 10; i++) {
final int it = i;
new Thread(()->{
c.get(""+it);
},"r"+it).start();
}
}
}
class Cache{
private volatile Map<String,String> map = new HashMap<>();
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void put(String key, String value){
try{
readWriteLock.writeLock().lock(); //写锁 lock
System.out.println(Thread.currentThread().getName()+" write \""+key+":"+value+"\"");
map.put(key,value);
System.out.println(Thread.currentThread().getName()+" write complete");
} finally {
readWriteLock.writeLock().unlock(); //写锁 unlock
}
}
public void get(String key){
try{
readWriteLock.readLock().lock(); //读锁 lock
System.out.println(Thread.currentThread().getName()+" read "+key);
map.get(key);
System.out.println(Thread.currentThread().getName()+" read complete");
} finally {
readWriteLock.readLock().unlock(); //读锁 unlock
}
}
}
10.阻塞队列
Deque 为双端队列
使用队列
添加,移出
四组API
有返回值,抛出异常 | 有返回值,不抛出异常 | 等待,阻塞 | 超时等待 | |
---|---|---|---|---|
添加 | add(obj) | offer(obj) | put(obj) | offer(obj, timeout, timeUnit) |
移除 | remove() | poll() | take() | poll(timeout, timeUnit) |
队首元素 | element() | peek() |
take, put方法线程安全
SynchronousQueue
同步队列,put,take
A blocking queue in which each insert operation must wait for a corresponding remove operation by another thread, and vice versa. A synchronous queue does not have any internal capacity, not even a capacity of one. You cannot peek at a synchronous queue because an element is only present when you try to remove it; you cannot insert an element (using any method) unless another thread is trying to remove it; you cannot iterate as there is nothing to iterate. The head of the queue is the element that the first queued inserting thread is trying to add to the queue; if there is no such queued thread then no element is available for removal and poll() will return null. For purposes of other Collection methods (for example contains), a SynchronousQueue acts as an empty collection. This queue does not permit null elements.
put和take匹配,
11.线程池
池化技术 -> 优化资源使用:提前准备好资源
线程池,连接池,内存池,对象池…
线程池好处:
- 降低资源的消耗
- 提高响应速度
- 方便管理
线程复用,可以控制最大并发数,管理线程
三大操作
ExecutorService pool = Executors.newSingleThreadExecutor();
// pool = Executors.newFixedThreadPool(int);
// pool = Executors.newCachedThreadPool();
pool.execute(Runnable);
pool.shutdown();
七大参数
//源码,本质是返回ThreadPoolExecutor对象
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
//源码,七大参数
public ThreadPoolExecutor(int corePoolSize,//核心线程池大小
int maximumPoolSize,//最大线程池大小
long keepAliveTime, //超时 没有调用释放
TimeUnit unit, //超时单位
BlockingQueue<Runnable> workQueue, //阻塞队列
ThreadFactory threadFactory, //线程工程,创建线程
RejectedExecutionHandler handler) //拒绝策略
//最大承载线程:maximumPoolSize + workQueue.size();
ExecutorService pool = new ThreadPoolExecutor(2,
5,
0,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory,
new ThreadPoolExecutor.AbortPolicy());
/**4种拒绝策略
* 1. ThreadPoolExecutor.AbortPolicy 丢弃,抛出异常
* 2. ThreadPoolExecutor.CallerRunsPolicy 拒绝,由提交的线程执行任务
* 3. ThreadPoolExecutor.DiscardPolicy 丢弃,不抛出异常
* 4. ThreadPoolExecutor.DiscardOldestPolicy 丢弃队列最前面的任务,重新提交被拒绝的任务
*/
最大线程池大小的定义
1.CPU密集型,定义为CPU核数 Runtime.getRuntime().availableProcessors();
2.IO密集型,定义为程序中十分耗IO的线程数+1
12.四大函数式接口
Lamda表达式,链式编程,函数式接口,Stream流式计算
四大函数式接口:位于java.util.function包下
- Consumer<T> void accept(T t) (消费型接口,有参数,无返回)
- Function<T,R> R apply(T t) (函数型接口,有参数,有返回)
- Predicate<T> boolean test(T t) (断定型接口,有参数,返回boolean)
- Supplier<T> T get() (供给型接口,无参数,有返回)
13.Stream流式计算
什么是Stream流式计算
import java.util.*;
public class Test {
public static void main(String[] args) {
User u1 = new User(1,"a",21);
User u2 = new User(2,"b",22);
User u3 = new User(3,"c",23);
User u4 = new User(4,"d",24);
User u5 = new User(6,"e",25);
List<User> list = Arrays.asList(u1,u2,u3,u4,u5);
/**筛选用户
* 1.ID为偶数
* 2.年龄大于23
* 3.用户名转为大写
* 4.用户名倒序
* 5.只输出一个用户
*/
list.stream()
.filter(u->u.getId()%2==0) //filter 参数: Predicate<? super T> predicate
.filter(u->u.getAge()>23)
.map(u->u.getName().toUpperCase())// map 参数: Function<? super T, ? extends R> mapper
.sorted(Comparator.reverseOrder())//sorted 参数: Comparator<? super T> comparator
.limit(1) // limit 参数: long maxSize
.forEach(System.out::println); //forEach 参数: Consumer<? super T> action
}
}
class User{
private int id;
private String name;
private int age;
public User(int id, String name, int age){
this.id = id;
this.name = name;
this.age = age;
}
public void setId(int id){
this.id = id;
}
public void setName(String name){
this.name = name;
}
public void setAge(int age){
this.age = age;
}
public int getId(){
return id;
}
public String getName(){
return name;
}
public int getAge(){
return age;
}
}
14. ForkJoin
将一个大的任务拆分成多个子任务进行并行处理,最后将子任务结果合并成最后的计算结果,并进行输出
ForkJoin特点:工作窃取(将小任务加到线程队列中,当没有任务执行时,再从一个随机线程的队列中偷一个并把它放在自己的队列中)
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;
public class TestForkJoin {
public static void test1(long start,long end){
long t1 = System.currentTimeMillis();
ForkJoinPool pool = new ForkJoinPool();
ForkJoinTask<Long> t = new Cal(start,end);
long sum = pool.invoke(t);
long t2 = System.currentTimeMillis();
System.out.println("sum="+sum+", cost:"+(t2-t1)+"ms");
}
public static void test2(long start,long end){
long t1 = System.currentTimeMillis();
long sum = LongStream.rangeClosed(start,end).parallel().reduce(0,Long::sum);
long t2 = System.currentTimeMillis();
System.out.println("sum="+sum+", cost:"+(t2-t1)+"ms");
}
public static void main(String[] args) {
long start = 1L;
long end = 10_0000_0000L;
test1(start,end);
test2(start,end);
}
}
class Cal extends RecursiveTask<Long>{
private long start;
private long end;
private long temp = 1000L;
public Cal(long start,long end){
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if(end-start<temp){
long sum = 0;
for(long i=start;i<=end;i++){
sum += i;
}
return sum;
}
long mid = (start+end)/2;
Cal left = new Cal(start,mid);
Cal right = new Cal(mid+1,end);
left.fork();
right.fork();
return left.join()+right.join();
}
}
15.异步回调
CompletableFuture<Void> c = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("asyn");
});
c.get();
16.JMM
volatile
volatile是java虚拟机提供的轻量级的同步机制
1.保证可见性
2.不保证原子性
3.禁止指令重排
JMM
java内存模型
关于JMM的一些同步约定:
1.线程解锁前,必须把共享变量立刻刷回主存
2.线程加锁前,必须读取主存中的最新值到工作内存中
3.加锁和解锁是同一把锁
-
- lock
- unlock
- read
- load
- use
- assign
- store
- write
java.util.concurrent.atomic包下原子封装类保证操作原子性
指令重排,编译器优化的结果
对单线程,指令重排导致程序结果正确
对多线程,指令重排可能导致程序结果不正确
内存屏障
17.单例模式
- 饿汉式
- 懒汉式
- 静态内部类
18.CAS
CAS: Compare And Swap
import java.util.concurrent.atomic.AtomicInteger;
public class Test {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(20);
atomicInteger.compareAndSet(20,21);
System.out.println(atomicInteger.get());
}
}
ABA问题:变量值由A变为B,再变为A,CAS将检测到变量的值未变化,实际上变化了
原子引用解决ABA问题(版本号)
AtomicStampedReference<Integer> atomicInteger
= new AtomicStampedReference<>(initialRef, //值
initialStamp); //版本号
// compareAndSet(期望值,新值,期望版本号,新版本号)
所有相同类型的包装类对象之间的比较,全部用equals方法,
说明:Integer var = ?在-128~127之间的赋值,Integer对象是在IntegeCache.cache产生,会复用已有的对象,这个区间的Integer可以直接使用==判断,这个区间之外的数据,都会在堆中产生,不会复用已有的对象
19.锁
19.1 公平锁,非公平锁
public ReentrantLock() {
sync = new NonfairSync(); //默认非公平锁
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
19.2 可重入锁
已获取锁的线程再次获取该锁不会造成死锁
lock锁使用必须配对 lock.loc() … lock.unlock()
lock.lock(); lock.lock(); //... lock.unlock(); lock.unlock();
19.3 自旋锁 (Spinlock)
当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环
19.4 死锁
排查 使用JDK/bin下工具
- jps -l 定位进程号
- jstack 进程号