1、8锁现象
2、集合类不安全
- CopyOnWriteArrayList
- CopyOnWriteArraySet
- ConcurrentHashMap
3、常用的辅助类
-
CountDownLatch
public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { // 总数是6 CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 0; i < 6; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName() + "go out"); countDownLatch.countDown(); }, String.valueOf(i)).start(); } countDownLatch.await(); System.out.println("close door"); } }
原理:
countDownLatch.countDown() :数量-1
countDownLatch.await():等待计数器归零,然后在向下执行
每次有线程调用countDown数量-1,假设计数器为0,countDownLatch.await()就会被唤醒,继续执行
-
CyclicBarrier
加法计数器
public class CyclicBarrierDemo { public static void main(String[] args) { /** * 集齐7颗龙珠召唤神龙 */ CyclicBarrier barrier = new CyclicBarrier(7, () -> { System.out.println("召唤神龙成功"); }); for (int i = 1; i <= 7; i++) { final int temp = i; new Thread(() -> { System.out.println(Thread.currentThread().getName() + "收集" + temp + "个龙珠"); try { barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }, String.valueOf(i)).start(); } } }
-
Semaphore
public class SemaphoreDemo { public static void main(String[] args) { Semaphore semaphore = new Semaphore(3); for (int i = 1; i <= 6; i++) { new Thread(() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + "抢到车位"); TimeUnit.SECONDS.sleep(2); System.out.println(Thread.currentThread().getName() + "离开车位"); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }, String.valueOf(i)).start(); } } }
原理:
semaphore.acquire():获取,假设如果已经满了,等待,等待被释放为止
semaphore.release():释放,会将当前的信号量释放,然后唤醒等待的线程
作用:多个共享资源互斥使用!并发限流,控制最大的线程数
4、读写锁
/**
* @Classname ReadWriteLockDemo
* @Date 2021/4/1 下午4:41
* @Created by fei.liu
*
* 独占锁(写锁):一次只能被一个线程持有
* 共享锁(读锁):多个线程可以同时占有
*
* 读-读:可以共存
* 读-写:不能共存
* 写-写:不能共存
*/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache cache = new MyCache();
// MyCacheLock cache = new MyCacheLock();
for (int i = 1; i <= 5; i++) {
final int tmp = i;
new Thread(() -> {
cache.put(tmp + "", tmp + "");
}, String.valueOf(i)).start();
}
for (int i = 1; i <= 5; i++) {
final int tmp = i;
new Thread(() -> {
cache.get(tmp + "");
}, String.valueOf(i)).start();
}
}
}
/**
* 自定义缓存 + 锁
*/
class MyCacheLock {
private volatile Map<String, Object> map = new HashMap<>();
private ReadWriteLock lock = new ReentrantReadWriteLock();
/**
* 写入的时候只希望同时只有一个线程写数据
* @param key
* @param value
*/
public void put(String key, Object value) {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入ok");
} catch (Exception e) {
e.getMessage();
} finally {
lock.writeLock().unlock();
}
}
/**
* 读取数据多线程可以同时读取
* @param key
* @return
*/
public Object get(String key) {
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "获取" + key);
return map.get(key);
} catch (Exception e) {
e.getMessage();
} finally {
lock.readLock().unlock();
}
return null;
}
}
/**
* 自定义缓存
*/
class MyCache {
private volatile Map<String, Object> map = new HashMap<>();
public void put(String key, Object value) {
System.out.println(Thread.currentThread().getName() + "写入" + key);
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "写入ok");
}
public Object get(String key) {
System.out.println(Thread.currentThread().getName() + "获取" + key);
return map.get(key);
}
}
5、阻塞队列
什么情况下我们会使用阻塞队列?
多线程并发处理、线程池
使用队列
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞 等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer() | put() | offer() |
移除 | remove | poll() | take() | poll() |
判断队列首 | element() | peek() | - | - |
6、线程池(重点)
程序的运行,本质:占用系统的资源!优化资源的使用 -> 池化技术
线程池、连接池、内存池、对象池……
池化技术:事先准备好一些资源,有人要用,就来我这里拿,用完之后还给我
线程池的好处:
- 降低资源消耗
- 降低响应速度
- 方便管理
线程复用、可以控制最大并发数、管理线程
线程池三大方法:
public class ThreadPoolDemo1 {
public static void main(String[] args) {
ExecutorService threadPool = Executors.newSingleThreadExecutor();// 单个线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5); // 创建一个固定线程池的大小
// ExecutorService threadPool = Executors.newCachedThreadPool(); // 可伸缩的
try {
for (int i = 0; i < 100; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "-ok");
});
}
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
// 线程池用完,程序结束,关闭线程池
threadPool.shutdown();
}
}
}
线程池7大参数:
源码分析:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public ThreadPoolExecutor(int corePoolSize, // 核心线程池大小
int maximumPoolSize, // 最大核心线程数大小
long keepAliveTime, // 超时了没有人调用就会释放
TimeUnit unit, // 超时单位
BlockingQueue<Runnable> workQueue, // 阻塞队列
ThreadFactory threadFactory, // 线程工厂:创建线程,一般不用动
RejectedExecutionHandler handler // 拒绝策略
) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
workQueue:SynchronousQueue,
LinkedBlockingDeque,
ArrayBlockingQueue
最大线程到底如何定义?
- CPU密集型:几核,就是几,可以保证CPU的效率最高
- IO 密集型:判断程序中十分耗IO的线程
线程池规则(假设任务队列没有大小限制):
- 如果线程数<=核心线程数量,那么直接启动一个核心线程来执行任务,不会放入队列中
- 如果线程数量>核心线程数,但<=最大线程数,并且队列是LinkedBlockingQueue的时候,超过核心线程数量的任务会被放在任务队列中排队
- 如果线程数量>核心线程数,但<=最大线程数,并且队列是SynchronousQueue的时候,线程池会创建新的线程执行任务,这些任务不会放在任务队列中。这些线程属于非核心线程,在任务完成后,闲置时间打到了超时时间就会被清除
- 如果线程数量>核心线程数,并且>最大线程数,并且队列是LinkedBlockingQueue的时候,超过核心线程的任务放在任务队列中排队。也就是LinkedBlockingQueue没有大小限制时,线程池最大线程数设置是无效的,他的线程数最多不会超过核心线程数
- 如果线程数量>核心线程数,并且>最大线程数,并且队列是SynchronousQueue的时候,会因为线程池拒绝添加任务而抛出异常
线程池规则(假设任务队列有大小限制):
- 当LinkedBlockQueue塞满时,新增的任务会直接直接创建新线程来执行,当创建的新线程超过最大线程数量就会抛出异常
- SynchronousQueue没有数量限制,因为他不保存任务,直接交给线程池去执行,任务超过最大线程数就会抛出异常
7、四大函数式接口(必须掌握)
新时代的程序员:lambda表达式、链式编程、函数式接口、Stream流式计算
- 函数式接口:只有一个方法的接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
// 超级多FunctionalInterface
// 简化编程模型,在新版本的框架底层大量应用
- 断定型接口:有一个输入参数,返回值只能是布尔值
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
}
- consumer:消费型接口:接受参数,没有返回值
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
- supplier:供给型接口:没有参数,只有返回值
@FunctionalInterface
public interface Supplier<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
8、Stream流式计算
什么是流式计算?
9、ForkJoin
什么是ForkJoin?
ForkJoin 在 JDK 1.7,并行执行任务!提高效率,大数据量!
ForkJoin特点:工作窃取
10、异步回调
Future 设计的初衷:对将来的某个事件的结果进行建模
public class FutureDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
// try {
// TimeUnit.SECONDS.sleep(2);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName() + " async");
// });
// System.out.println("1111");
//
// try {
// completableFuture.get(); //获取执行结果
// } catch (InterruptedException e) {
// e.printStackTrace();
// } catch (ExecutionException e) {
// e.printStackTrace();
// }
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "supply");
int i = 10 / 0;
return 1024;
});
System.out.println(future.whenComplete((t, u) -> {
System.out.println("t = " + t);
System.out.println("u = " + u);
}).exceptionally((e) -> {
System.out.println(e.getMessage());
return 233;
}).get());
}
}
16、JMM
请谈谈对Volatile的理解?
volatile是Java虚拟机提供的轻量级的同步机制
- 保证可见性
- 不保证原子性
- 禁止指令重排
如何保证可见性?什么是JMM?
JMM:java内存模型,不存在的东西,概念,约定!
关于JMM的一些同步的约定:
- 线程解锁前,必须把共享变量立刻刷回主存
- 线程加锁前,必须读取主存中的最新值到工作内存中
- 加锁和解锁是同一把锁
11、volatile
- 保证可见性
- 不保证原子性
- 禁止指令重排
- 保证特定的操作的执行顺序
- 可以保证某些变量的内存可见性(利用这些特性就可以保证volatile的可见性)
原子类:底层CAS
指令重排:编写的程序,计算机并不是按照写的那样去执行的
源代码 -> 编译器优化的重排 -> 指令并行也可能重排 -> 内存系统也会重排 -> 执行
12、彻底玩转单例模式
13、深入理解CAS
什么是CAS?
CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么则执行操作,如果不是就一直循环!
缺点:
- 循环会耗时
- 一次性只能保证一个共享变量的原子性
- ABA问题
14、原子引用
public class AtomicReferenceDemo { public static void main(String[] args) { // AtomicStampedReference 注意:如果泛型是包装类,注意对象的引用问题 AtomicStampedReference<Integer> reference = new AtomicStampedReference<>(1, 1); new Thread(() -> { int stamp = reference.getStamp(); // 获取版本号 System.out.println("a stamp = " + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } reference.compareAndSet(1, 2, reference.getStamp(), reference.getStamp() + 1); System.out.println("a 2 stamp = " + reference.getStamp()); reference.compareAndSet(2, 1, reference.getStamp(), reference.getStamp() + 1); System.out.println("a 3 stamp = " + reference.getStamp()); }, "a").start(); new Thread(() -> { int stamp = reference.getStamp(); // 获取版本号 System.out.println("b stamp = " + stamp); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } reference.compareAndSet(1, 66, stamp, stamp + 1); System.out.println("b 2 stamp = " + reference.getStamp()); }, "b").start(); }}
15、各种锁的理解
-
公平锁、非公平锁
公平锁:非常公平,不能插队;必须先来后到
非公平锁:不公平,可以插队;
public ReentrantLock() { sync = new NonfairSync();}public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync();}
-
可重入锁
可重入锁(递归锁)
public class ReEnterLock { public static void main(String[] args) { Phone phone = new Phone(); new Thread(() -> { phone.sms(); }, "A").start(); new Thread(() -> { phone.sms(); }, "B").start(); }}class Phone { public synchronized void sms() { System.out.println(Thread.currentThread().getName() + " sms"); call(); } public synchronized void call() { System.out.println(Thread.currentThread().getName() + "call"); }}
-
自旋锁
spinlock:自定义自旋锁测试
public class SpinLockDemo { AtomicReference<Thread> atomicReference = new AtomicReference<>(); // 加锁 public void myLock() { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "--> my lock"); // 自旋锁 while (!atomicReference.compareAndSet(null, thread)) { } } // 解锁 public void myUnLock() { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "--> my un lock"); atomicReference.compareAndSet(thread, null); }}
16、死锁
死锁测试,怎么样排除死锁?
public class DeadLockDemo { public static void main(String[] args) { String lockA = "lockA"; String lockB = "lockB"; new Thread(new MyThread(lockA, lockB), "T1").start(); new Thread(new MyThread(lockB, lockA), "T2").start(); }}class MyThread implements Runnable { private String lockA; private String lockB; public MyThread(String lockA, String lockB) { this.lockA = lockA; this.lockB = lockB; } @Override public void run() { synchronized (lockA) { System.out.println(Thread.currentThread().getName() + "lock" + lockA + ", want " + lockB); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (lockB) { System.out.println(Thread.currentThread().getName() + "lock" + lockB + ", want " + lockA); } } }}
16、并发编程中的三个概念
- 原子性
- 可见性
- 有序性
17、happens-before原则
- 程序次序院子:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
- 锁定原则:一个unLock操作先行于后面对同一个锁的lock操作
- volatile变量原则:对一个变量的写操作先行发生于后面对这个变量的读操作
- 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
- 线程启动规则:Thread对象的start()方法先行于此线程的每一个动作
- 线程中断规则:对线程的interruput()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
- 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已终止执行
- 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始
18、死锁
public class DeadLock implements Runnable { public int flag = 1; private static Object o1 = new Object(), o2 = new Object(); @Override public void run() { System.out.println("flag = " + flag); if (flag == 1) { synchronized (o1) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o2) { System.out.println("1"); } } } if (flag == 2) { synchronized (o2) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (o1) { System.out.println(2); } } } } public static void main(String[] args) { DeadLock dt1 = new DeadLock(); DeadLock dt2 = new DeadLock(); dt1.flag = 1; dt2.flag = 2; new Thread(dt1).start(); new Thread(dt2).start(); }}
19、Callable
1、通过Thread来提交任务
public class CallableAndFuture { public static void main(String[] args) { Callable<Integer> callable = new Callable<Integer>() { @Override public Integer call() throws Exception { return new Random().nextInt(100); } }; FutureTask<Integer> futureTask = new FutureTask<>(callable); new Thread(futureTask).start(); try { Thread.sleep(5000); System.out.println(futureTask.get()); } catch (Exception e) { e.printStackTrace(); } }}
2、通过ExecutorService的submit执行Callable
public class CallableAndFuture { public static void main(String[] args) { ExecutorService threadPool = Executors.newSingleThreadExecutor(); Future<Integer> future = threadPool.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { return new Random().nextInt(100); } }); try { Thread.sleep(5000); System.out.println(future.get()); } catch (Exception e) { e.printStackTrace(); } }}
3、执行多个带返回值的任务,并取得多个返回值
public class CallableAndFuture { public static void main(String[] args) { ExecutorService threadPool = Executors.newCachedThreadPool(); CompletionService<Integer> cs = new ExecutorCompletionService<>(threadPool); for (int i = 1; i < 5; i++) { final int taskId = i; cs.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { return taskId; } }); } for (int i = 1; i < 5; i++) { try { System.out.println(cs.take().get()); } catch (Exception e) { e.printStackTrace(); } } }}
20、ListenableFuture根据结果,主动中断流程
public class ListenableFutureDemo { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(2); ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(executor); final Queue<ListenableFuture<Boolean>> currentMiningTasks = new ConcurrentLinkedQueue<>(); currentMiningTasks.add(listeningExecutorService.submit(new A())); currentMiningTasks.add(listeningExecutorService.submit(new A())); currentMiningTasks.add(listeningExecutorService.submit(new A(true))); currentMiningTasks.add(listeningExecutorService.submit(new A())); currentMiningTasks.add(listeningExecutorService.submit(new A())); currentMiningTasks.add(listeningExecutorService.submit(new A())); currentMiningTasks.add(listeningExecutorService.submit(new A())); currentMiningTasks.add(listeningExecutorService.submit(new A())); currentMiningTasks.add(listeningExecutorService.submit(new A())); currentMiningTasks.add(listeningExecutorService.submit(new A())); currentMiningTasks.add(listeningExecutorService.submit(new A())); // 一定要执行这个方法, listeningExecutorService.shutdown(); executor.shutdown(); for (final ListenableFuture<Boolean> task : currentMiningTasks) { try { System.out.println("结果..." + task.get()); if (task.get()) { for (final ListenableFuture<Boolean> t : currentMiningTasks) { if (t != null && !t.isCancelled()) { t.cancel(true); } } } } catch (Exception e) { e.printStackTrace(); } } while (true) { if (listeningExecutorService.isTerminated() && executor.isTerminated()) { System.out.println("listeningExecutorService executor 结束"); break; } } System.out.println("结束。。。。。"); }}class A implements Callable<Boolean> { boolean flag = false; public A() { } public A(boolean flag) { this.flag = flag; } @Override public Boolean call() throws Exception { Thread.sleep(1000); return flag; }}