为什么要使用线程池
解决上面两个问题的思路
线程池的好处
线程池适合应用的场合
1.如果线程小于corePoolSize,即使其他工作线程处于空闲状态,也会创建一个新线程来运行任务
2.如果线程数等于(或大于)corePoolSize但少于maximumPoolSize,则将任务放入队列
3.如果队列已满,并且线程数小于maxPoolSize,则创建一个新线程来运行任务
4.如果队列已满,并且线程数大于或等于maxPoolSize,则拒绝该任务
是否需要增加线程的判断顺序是:
1.corePoolSize
2.workQueue
3.maxPoolSize
增减线程的特点
1.通过设置corePoolSize和maximumPoolSize相同,就可以创建固定大小的线程池
2.线程池希望保持较少的线程数,并且只有在负载变得很大时才增加它
3.通过设置maximumPoolSize为很高的值,例如Integer.MAX_VALUE,可以允许线程池容纳任意数量的并发任务
4.是只有在队列填满时才创建多余corePoolSize的线程,所以如果你使用的是无界队列(例如LinkedBlockingQueue),那么线程数就不会超过corePoolSize
keepAliveTime
如果线程池当前的线程数多于corePoolSize,那么如果多于的线程空闲时间超过keepAliveTime,他们就会被终止
ThreadFactory用来创建线程
新的线程是由ThreadFactory创建的,默认使用Executors.defaultThreadFactory(),创建出来的线程都在同一个线程组,拥有同样的NORM_PRIORITY优先级并且都不是守护线程。如果自己指定ThreadFactory,那么就可以改变线程名、线程组、优先级、是否是守护线程等
newFixedThreadPool
由于传进去的LinkedBlockingQueue是没有容量上限的,所有当请求数越来越多,并且无法及时处理完毕的时候,也就是请求堆积的时候,会容易造成占用大量的内存,可能会导致OOM
newSingleThreadExecutor
1.单线程的线程池:它只会用唯一的工作线程来执行任务
2.它的原理和FixedThreadPool是一样的,但是此时的线程数量被设置为了1
可以看出,这是和刚才的newFixedThreadPool的原理基本一样,只不过把线程数直接设置成了1,所以这会导致同样的问题,也就是当请求堆积的时候,可能会占用大量的内存
newCachedThreadPool
1.可缓存线程池
2.特点:无界线程池,具有自动回收多余线程的功能
这是的弊端在于第二个参数maximumPoolSize被设置为了Integer.MAX_VALUE,这可能会创建数量非常多的线程,甚至导致OOM
newScheduledThreadPool
支持定时及周期性任务执行的线程池
正确的创建线程池的方法
线程池里的线程数量设定为多少比较合适
停止线程池的正确方法
任务太多,怎么拒绝?
拒绝时机
1.当Executor关闭时,提交新任务会被拒绝
2.以及当Executor对最大线程和工作队列容量使用有限边界并且已经饱和时
4种拒绝策略
1.AbortPolicy 直接抛出异常
2.DiscardPolicy 默默丢弃任务
3.DiscardOldestPolicy 丢弃最老的
4.CallerRunsPolicy 让主线程去运行避免了业务损失,可以让提交速度降低下来这是一种负反馈
Executor家族
ExecutorService executorService = Executors.newFixedThreadPool();
线程池实现任务复用的原理
线程池状态
使用线程池的注意点
典型场景1:每个线程需要一个独享的对象
public class ThreadLocalNormalUsage00 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage00().date(10);
System.out.println(date);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage00().date(104707);
System.out.println(date);
}
}).start();
}
public String date(int seconds){
//参数的单位是毫秒, 从1970.1.1 00:00:00 GMT计时
Date date = new Date(1000 * seconds);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
return dateFormat.format(date);
}
}
延伸出10个,那就有10个线程和10个SimpleDateFormat,写法不优雅
/**
* 描述: 10个线程打印日期
*/
public class ThreadLocalNormalUsage01 {
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 30; i++) {
int finalI = i;
new Thread(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage01().date(finalI);
System.out.println(date);
}
}).start();
Thread.sleep(100);
}
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
Date date = new Date(1000 * seconds);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return dateFormat.format(date);
}
}
/**
* 描述: 1000个打印日期的任务,用线程池来执行
*/
public class ThreadLocalNormalUsage02 {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
int finalI = i;
threadPool.submit(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage02().date(finalI);
System.out.println(date);
}
});
}
threadPool.shutdown();
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
Date date = new Date(1000 * seconds);
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return dateFormat.format(date);
}
}
/**
* 描述: 1000个打印日期的任务,用线程池来执行
*/
public class ThreadLocalNormalUsage03 {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
int finalI = i;
threadPool.submit(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage03().date(finalI);
System.out.println(date);
}
});
}
threadPool.shutdown();
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
Date date = new Date(1000 * seconds);
return dateFormat.format(date);
}
}
所有线程都共用同一个simpleDateFormat对象发生了线程安全问题
/**
* 描述: 加锁来解决线程安全问题
*/
public class ThreadLocalNormalUsage04 {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
int finalI = i;
threadPool.submit(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage04().date(finalI);
System.out.println(date);
}
});
}
threadPool.shutdown();
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
Date date = new Date(1000 * seconds);
String s = null;
synchronized (ThreadLocalNormalUsage04.class) {
s = dateFormat.format(date);
}
return s;
}
}
SimpleDateFormat的进化之路
更好的解决方案是使用ThreadLocal
/**
* 描述: 利用ThreadLocal,给每个线程分配自己的dateFormat对象,保证了线程安全,高效利用内存
*/
public class ThreadLocalNormalUsage05 {
public static ExecutorService threadPool = Executors.newFixedThreadPool(10);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 1000; i++) {
int finalI = i;
threadPool.submit(new Runnable() {
@Override
public void run() {
String date = new ThreadLocalNormalUsage05().date(finalI);
System.out.println(date);
}
});
}
threadPool.shutdown();
}
public String date(int seconds) {
//参数的单位是毫秒,从1970.1.1 00:00:00 GMT计时
Date date = new Date(1000 * seconds);
// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
SimpleDateFormat dateFormat = ThreadSafeFormatter.dateFormatThreadLocal2.get();
return dateFormat.format(date);
}
}
class ThreadSafeFormatter {
public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = new ThreadLocal<SimpleDateFormat>() {
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
}
};
//lambda表达式
public static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal2 = ThreadLocal
.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
}
典型场景2 实例:当前用户信息需要被线程内所有方法共享
一个比较繁琐的解决方案是把user作为参数层层传递,从service-1()传到service-2(),再从service-2()传到service-3(),以此类推,但是这样做会导致代码冗杂且不易维护
方法
用ThreadLocal保存一些业务内容(用户权限信息、从用户系统获取到的用户名、user ID等)
这些信息在用一线程内相同,但是不用的线程使用的业务内容是不相同的
当多线程同时工作时,我们需要保证线程安全,可以用synchronized,也可以用ConcurrentHashMap,但无论用什么,都会对性能有所影响
更好的办法是使用ThreadLocal,这样无需synchronized,可以在不影响性能的情况下,也无需层层传递参数,就可达到保存当前线程对应的用户信息的目的
强调的是同一个请求内(同一个线程内)不同方法间的共享
不需要重写**initialValue()方法,但是必须手动调用set()**方法
/**
* 描述: 演示ThreadLocal用法2:避免传递参数的麻烦
*/
public class ThreadLocalNormalUsage06 {
public static void main(String[] args) {
new Service1().process("");
}
}
class Service1 {
public void process(String name) {
User user = new User("超哥");
UserContextHolder.holder.set(user);
new Service2().process();
}
}
class Service2 {
public void process() {
User user = UserContextHolder.holder.get();
ThreadSafeFormatter.dateFormatThreadLocal.get();
System.out.println("Service2拿到用户名:" + user.name);
new Service3().process();
}
}
class Service3 {
public void process() {
User user = UserContextHolder.holder.get();
System.out.println("Service3拿到用户名:" + user.name);
UserContextHolder.holder.remove();
}
}
class UserContextHolder {
public static ThreadLocal<User> holder = new ThreadLocal<>();
}
class User {
String name;
public User(String name) {
this.name = name;
}
}
ThreadLocal的两个作用
根据共享对象的生成时机不同,选择initialValue或set来保存对象
场景一:initialValue 在ThreadLocal第一次get的时候把对象给初始化出来,对象的初始化时机可以由我们控制
场景二:set 如果需要保存到ThreadLocal里的对象的生成时机不由我们随意控制,例如拦截器生成的用户信息,用ThreadLocal.set直接放到我们的ThreadLocal中取,以便后续使用。
T initialValue(): 初始化
void set(T t): 为这个线程设置一个新值
T get(): 得到这个线程对应的value。如果是首次调用get(),则会调用initialize来得到这个值
void remove() : 删除对应这个线程的值
键:这个ThreadLocal
值:实际需要的成员变量,比如user或者simpleDateFormat对象
两种使用场景殊途同归
内存泄漏
Value的泄漏
ThreadLocalMap的每个Entey都是一个对key的弱引用,同时,每个Entry都包含了一个对value的强引用
正常情况下,当线程终止,保存在ThreadLocal里的value会被垃圾回收,因为没有任何强引用了
因为value和Thread之间还存在这个强引用链路,所以导致value无法回收,就可能会出现OOM
JDK已经考虑到了这个问题,所以在set,remove,rehash方法中会扫描key为null的Entry,并把对应的value设置为null,这样value对象就可以被回收
但是如果一个ThreadLocal不被使用,那么实际上set,remove,rehash方法也不会被调用,如果同时线程又不停止,那么调用链就一直存在,那么就导致了value的内存泄漏
如何避免内存泄漏(阿里规约)
空指针异常
共享对象
如果可以不适用ThreadLocal就解决问题,那么就不要强行使用
优先使用框架的支持,而不是自己创造
实际应用场景 - 在Spring中的实例分析
Lock简介、地位、作用
为什么需要Lock?
为什么synchronized不够用?
Lock主要方法介绍
lock()
/**
* 描述: Lock不会像synchronized一样,异常的时候自动释放锁,所以最佳实践是,finally中释放锁,以便保证发生异常的时候锁一定被释放
*/
public class MustUnlock {
private static Lock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock();
try{
//获取本锁保护的资源
System.out.println(Thread.currentThread().getName()+"开始执行任务");
}finally {
lock.unlock();
}
}
}
tryLock()
/**
* 描述: 用tryLock来避免死锁
*/
public class TryLockDeadlock implements Runnable {
int flag = 1;
static Lock lock1 = new ReentrantLock();
static Lock lock2 = new ReentrantLock();
public static void main(String[] args) {
TryLockDeadlock r1 = new TryLockDeadlock();
TryLockDeadlock r2 = new TryLockDeadlock();
r1.flag = 1;
r1.flag = 0;
new Thread(r1).start();
new Thread(r2).start();
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (flag == 1) {
try {
if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程1获取到了锁1");
Thread.sleep(new Random().nextInt(1000));
if (lock2.tryLock(800, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程1获取到了锁2");
System.out.println("线程1成功获取到了两把锁");
break;
} finally {
lock2.unlock();
}
} else {
System.out.println("线程1获取锁2失败,已重试");
}
} finally {
lock1.unlock();
Thread.sleep(new Random().nextInt(1000));
}
} else {
System.out.println("线程1获取锁1失败,已重试");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (flag == 0) {
try {
if (lock2.tryLock(3000, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程2获取到了锁2");
Thread.sleep(new Random().nextInt(1000));
if (lock1.tryLock(800, TimeUnit.MILLISECONDS)) {
try {
System.out.println("线程2获取到了锁1");
System.out.println("线程2成功获取到了两把锁");
break;
} finally {
lock1.unlock();
}
} else {
System.out.println("线程2获取锁1失败,已重试");
}
} finally {
lock2.unlock();
Thread.sleep(new Random().nextInt(1000));
}
} else {
System.out.println("线程2获取锁2失败,已重试");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public class LockInterruptibly implements Runnable {
private Lock lock = new ReentrantLock();
public static void main(String[] args) {
LockInterruptibly lockInterruptibly = new LockInterruptibly();
Thread thread0 = new Thread(lockInterruptibly);
Thread thread1 = new Thread(lockInterruptibly);
thread0.start();
thread1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread1.interrupt();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "尝试获取锁");
try {
lock.lockInterruptibly();
try {
System.out.println(Thread.currentThread().getName() + "获取到了锁");
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "睡眠期间被中断了");
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + "释放了锁");
}
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "获得锁期间被中断了");
}
}
}
可见性保证
为什么诞生非互斥同步锁
什么是乐观锁和悲观锁
悲观锁
乐观锁
典型例子
开销对比
两种锁各自的使用场景:各有千秋
/**
* 描述: 演示多线程预定电影院座位
*/
public class CinemaBookSeat {
private static ReentrantLock lock = new ReentrantLock();
private static void bookSeat() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "开始预定座位");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "完成预定座位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
new Thread(() -> bookSeat()).start();
new Thread(() -> bookSeat()).start();
new Thread(() -> bookSeat()).start();
new Thread(() -> bookSeat()).start();
}
}
/**
* 描述: 演示ReentrantLock的基本用法,演示被打断
*/
public class LockDemo {
public static void main(String[] args) {
new LockDemo().init();
}
private void init() {
final Outputer outputer = new Outputer();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("悟空");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("大师兄");
}
}
}).start();
}
static class Outputer {
Lock lock = new ReentrantLock();
//字符串打印方法,一个个字符的打印
public void output(String name) {
int len = name.length();
lock.lock();
try {
for (int i = 0; i < len; i++) {
System.out.print(name.charAt(i));
}
System.out.println("");
} finally {
lock.unlock();
}
}
}
}
可重入性质
避免死锁、提升封装性
ReentrantLock方法介绍
什么是公平和非公平
为什么要有非公平锁
公平的情况(以ReentrantLock为例)
不公平的情况(以ReentrantLock为例)
特例
对比公平和非公平的优缺点
公平锁
不公平锁
什么是共享锁和排他锁
读写锁的作用
读写锁的规则
读写锁的规则
ReentrantReadWriteLock具体用法
读锁插入策略
(非公平) 1.写锁可以随时插队
2.读锁仅在等待队列头结点不是想获取写锁的线程的时候可以插队
自旋锁的缺点
自旋锁的适用场景
Java虚拟机对锁的优化
个人写代码优化锁和提高并发性能
AtomicInteger常用方法
**
* 描述: 演示AtomicInteger的基本用法,对比非原子类的线程安全问题,使用了原子类之后,不需要加锁,也可以保证线程安全。
*/
public class AtomicIntegerDemo1 implements Runnable {
private static final AtomicInteger atomicInteger = new AtomicInteger();
public void incrementAtomic() {
atomicInteger.getAndAdd(-90);
}
private static volatile int basicCount = 0;
public synchronized void incrementBasic() {
basicCount++;
}
public static void main(String[] args) throws InterruptedException {
AtomicIntegerDemo1 r = new AtomicIntegerDemo1();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("原子类的结果:" + atomicInteger.get());
System.out.println("普通变量的结果:" + basicCount);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
incrementAtomic();
incrementBasic();
}
}
}
/**
* 描述: 演示原子数组的使用方法
*/
public class AtomicArrayDemo {
public static void main(String[] args) {
AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(1000);
Incrementer incrementer = new Incrementer(atomicIntegerArray);
Decrementer decrementer = new Decrementer(atomicIntegerArray);
Thread[] threadsIncrementer = new Thread[100];
Thread[] threadsDecrementer = new Thread[100];
for (int i = 0; i < 100; i++) {
threadsDecrementer[i] = new Thread(decrementer);
threadsIncrementer[i] = new Thread(incrementer);
threadsDecrementer[i].start();
threadsIncrementer[i].start();
}
// Thread.sleep(10000);
for (int i = 0; i < 100; i++) {
try {
threadsDecrementer[i].join();
threadsIncrementer[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
for (int i = 0; i < atomicIntegerArray.length(); i++) {
// if (atomicIntegerArray.get(i)!=0) {
// System.out.println("发现了错误"+i);
// }
System.out.println(atomicIntegerArray.get(i));
}
System.out.println("运行结束");
}
}
class Decrementer implements Runnable {
private AtomicIntegerArray array;
public Decrementer(AtomicIntegerArray array) {
this.array = array;
}
@Override
public void run() {
for (int i = 0; i < array.length(); i++) {
array.getAndDecrement(i);
}
}
}
class Incrementer implements Runnable {
private AtomicIntegerArray array;
public Incrementer(AtomicIntegerArray array) {
this.array = array;
}
@Override
public void run() {
for (int i = 0; i < array.length(); i++) {
array.getAndIncrement(i);
}
}
}
/**
* 描述: 演示AtomicIntegerFieldUpdater的用法
*/
public class AtomicIntegerFieldUpdaterDemo implements Runnable{
static Candidate tom;
static Candidate peter;
public static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater
.newUpdater(Candidate.class, "score");
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
peter.score++;
scoreUpdater.getAndIncrement(tom);
}
}
public static class Candidate {
volatile int score;
}
public static void main(String[] args) throws InterruptedException {
tom=new Candidate();
peter=new Candidate();
AtomicIntegerFieldUpdaterDemo r = new AtomicIntegerFieldUpdaterDemo();
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("普通变量:"+peter.score);
System.out.println("升级后的结果"+ tom.score);
}
}
/**
* 描述: 演示高并发场景下,LongAdder比AtomicLong性能好
*/
public class AtomicLongDemo {
public static void main(String[] args) throws InterruptedException {
AtomicLong counter = new AtomicLong(0);
ExecutorService service = Executors.newFixedThreadPool(20);
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
service.submit(new Task(counter));
}
service.shutdown();
while (!service.isTerminated()) {
}
long end = System.currentTimeMillis();
System.out.println(counter.get());
System.out.println("AtomicLong耗时:" + (end - start));
}
private static class Task implements Runnable {
private AtomicLong counter;
public Task(AtomicLong counter) {
this.counter = counter;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
counter.incrementAndGet();
}
}
}
}
/**
* 描述: 演示高并发场景下,LongAdder比AtomicLong性能好
*/
public class LongAdderDemo {
public static void main(String[] args) throws InterruptedException {
LongAdder counter = new LongAdder();
ExecutorService service = Executors.newFixedThreadPool(20);
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
service.submit(new Task(counter));
}
service.shutdown();
while (!service.isTerminated()) {
}
long end = System.currentTimeMillis();
System.out.println(counter.sum());
System.out.println("LongAdder耗时:" + (end - start));
}
private static class Task implements Runnable {
private LongAdder counter;
public Task(LongAdder counter) {
this.counter = counter;
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
counter.increment();
}
}
}
}
这里演示多线程情况下AtomicLong的性能,有16个线程对同一个AtomicLong累加
由于竞争很激烈,每一个加法,都要flush和refresh,导致很耗费资源
在内部,这个LongAdder的实现原理和刚才的AtomicLong是有不同的,刚才的AtomicLong的实现原理是,每一次加法都需要做同步,所以在高并发的时候会导致冲突比较多,也就降低了效率
而此时的LongAdder,每个线程会有自己的一个计数器,仅用来在自己线程内计数,这样一来就不会和其他线程的计算器干扰
LongAdder带来的改进和原理
对比AtomicLong和Longadder
/**
* 描述: 演示LongAccumulator的用法
*/
public class LongAccumulatorDemo {
public static void main(String[] args) {
LongAccumulator accumulator = new LongAccumulator((x, y) -> 2 + x * y, 1);
ExecutorService executor = Executors.newFixedThreadPool(8);
IntStream.range(1, 10).forEach(i -> executor.submit(() -> accumulator.accumulate(i)));
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println(accumulator.getThenReset());
}
}
**
* 描述: 模拟CAS操作,等价代码
*/
public class SimulatedCAS {
private volatile int value;
public synchronized int compareAndSwap(int expectedValue, int newValue) {
int oldValue = value;
if (oldValue == expectedValue) {
value = newValue;
}
return oldValue;
}
}
Unsafe类
Unsafe类中的compareAndSwapInt方法
1.方法中先想办法拿到变量value在内存中的地址
2.通过Atomic::cmpxchg实现原子性的比较和替换,其中参数x是即将更新的值,参数e是原内存的值。至此,最终完成了CAS的全过程
final修饰变量
final修饰变量:赋值时机
final修饰:3种变量 赋值时机
第一种是在声明变量的等号右边直接赋值
第二种就是构造函数中赋值
第三就是在类的初始代码块中赋值(不常用)
如果不采用第一种赋值方法,那么就必须在第2、3种挑一个来赋值,而不能不赋值,这是final语法所规定的
两个赋值时机:除了在声明变量的等号右边直接赋值外,static final变量还可以用static初始代码块赋值,但是不能用普通初始代码块赋值
- 和前面两种不同,由于这里的变量是在方法里的,所以没有构造函数,也不存在初始代码块
- final local variable不规定赋值时机,只要求在使用前必须赋值,这和方法中的非final变量的要求也是一样的
为什么要规定赋值时机?
如果初始化不赋予值,后续赋值,就是从null变成你的赋值,这样就违反final不变的原则了
构造方法不允许final修饰
不可被重写,也就是不能被override,即便是子类有同样名字的方法,那也不是override,这个和static方法是一个道理
引申:static方法不能被重写
- 对于基本数据类型,确实被final修饰后就具有不可变性
- 但是对于对象类型,需要该对象保证自身被创建后,状态永远不会变才可以
满足以下条件时,对象才是不可变的
把变量写在线程内部 - 栈封闭
/**
* 描述: 演示栈封闭的两种情况,基本变量和对象 先演示线程争抢带来错误结果,然后把变量放到方法内,情况就变了
*/
public class StackConfinement implements Runnable {
int index = 0;
public void inThread() {
int neverGoOut = 0;
for (int i = 0; i < 10000; i++) {
neverGoOut++;
}
System.out.println("栈内保护的数字是线程安全的:" + neverGoOut);
}
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
index++;
}
inThread();
}
public static void main(String[] args) throws InterruptedException {
StackConfinement r1 = new StackConfinement();
Thread thread1 = new Thread(r1);
Thread thread2 = new Thread(r1);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(r1.index);
}
}
古老和过时的同步容器
ArrayList 和 HashMap
ConcurrentHashMap和CopyOnWriteArrayList
Map简介
为什么需要ConcurrentHashMap
它是通过一个锁来保证不同线程之间的并发访问的,但是由于synchronized对于并发量高的时候性能并不理想
同时put碰撞导致数据丢失(如果多个线程同时put,可能计算出的hash值是一样的,那么这两个key会放在同一个位置。但是两个线程都放在同一个位置,会有一个人是丢失的)
同时put扩容导致数据丢失(如果多个同时put并且同时发现需要扩容,扩容之后的数组也只有一个会保留下来)
多线程同时扩容可能造成循环链表导致CPU100%
JDK1.8的ConcurrentHashMap实现和源码分析
putVal流程
判断key value不为空
计算hash值
根据对应位置节点的类型,来赋值,或者helpTransfer,或者增长链表,或者给红黑树增加节点
检查满足阈值就“红黑树化”
返回oldVal
get流程
计算hash值
找到对应的位置,根据情况进行:
直接取值
红黑树里找值
遍历链表取值
返回找到的结果
对比1.7和1.8的优缺点,为什么要把1.7的结构改为1.8
ConcurrentHashMap也不是线程安全的?
不是用了ConcurrentHashMap你所做的任何事情都是线程安全的
所保证的是多个线程同时put数据不会错乱,在HashMap中多个数据同时put,内部的数据会发生错误导致结果不可预知
/**
* 描述: 组合操作并不保证线程安全
*/
public class OptionsNotSafe implements Runnable {
private static ConcurrentHashMap<String, Integer> scores = new ConcurrentHashMap<String, Integer>();
public static void main(String[] args) throws InterruptedException {
scores.put("小明", 0);
Thread t1 = new Thread(new OptionsNotSafe());
Thread t2 = new Thread(new OptionsNotSafe());
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(scores);
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
while (true) {
Integer score = scores.get("小明");
Integer newScore = score + 1;
boolean b = scores.replace("小明", score, newScore);
if (b) {
break;
}
}
}
}
}
诞生的历史和原因
使用场景
读写规则
实现原理
缺点
为什么要使用队列
什么是阻塞队列
阻塞功能: 最有特色的两个带有阻塞功能的方法是:
take() 方法:获取并移除队列的头结点,一旦如果执行take的时候,队列里无数据,则阻塞,直到队列里有数据
put() 方法:插入元素。但是如果队列已满,那么就无法继续插入,则阻塞,直到队列里有了空闲空间
是否有界(容量有多大) : 这是一个非常重要的属性,无界队列以为着里面可以容纳非常多(Integer.MAX_VALUE,约为2^31,是一个非常大的数,可以近似认为是无限容量)
阻塞队列和线程池的关系:阻塞队列是线程池的重要组成部分
主要方法介绍
ArrayBlockingQueue
public class ArrayBlockingQueueDemo {
public static void main(String[] args) {
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(3);
Interviewer r1 = new Interviewer(queue);
Consumer r2 = new Consumer(queue);
new Thread(r1).start();
new Thread(r2).start();
}
}
class Interviewer implements Runnable {
BlockingQueue<String> queue;
public Interviewer(BlockingQueue queue) {
this.queue = queue;
}
@Override
public void run() {
System.out.println("10个候选人都来啦");
for (int i = 0; i < 10; i++) {
String candidate = "Candidate" + i;
try {
queue.put(candidate);
System.out.println("安排好了" + candidate);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
queue.put("stop");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer implements Runnable {
BlockingQueue<String> queue;
public Consumer(BlockingQueue queue) {
this.queue = queue;
}
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
String msg;
try {
while(!(msg = queue.take()).equals("stop")){
System.out.println(msg + "到了");
}
System.out.println("所有候选人都结束了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
LinkedBlockingQueue
PriorityBlockingQueue
SynchronousQueue
DelayQueue
非阻塞队列
如何选择适合自己的队列
CountDownLatch类的作用
类的主要方法介绍
- CountDownLatch(int count):仅有一个**构造函数**,参数cout为需要倒数的数值
两个典型用法
注意点
总结
/**
* 描述: 工厂中,质检,5个工人检查,所有人都认为通过,才通过
*/
public class CountDownLatchDemo1 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(5);
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int no = i + 1;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println("No." + no + "完成了检查。");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}
};
service.submit(runnable);
}
System.out.println("等待5个人检查完.....");
latch.await();
System.out.println("所有人都完成了工作,进入下一个环节。");
}
}
/**
* 描述: 模拟100米跑步,5名选手都准备好了,只等裁判员一声令下,所有人同时开始跑步。
*/
public class CountDownLatchDemo2 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch begin = new CountDownLatch(1);
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int no = i + 1;
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("No." + no + "准备完毕,等待发令枪");
try {
begin.await();
System.out.println("No." + no + "开始跑步了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.submit(runnable);
}
//裁判员检查发令枪...
Thread.sleep(5000);
System.out.println("发令枪响,比赛开始!");
begin.countDown();
}
}
/**
* 描述: 模拟100米跑步,5名选手都准备好了,只等裁判员一声令下,所有人同时开始跑步。当所有人都到终点后,比赛结束。
*/
public class CountDownLatchDemo1And2 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch begin = new CountDownLatch(1);
CountDownLatch end = new CountDownLatch(5);
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int no = i + 1;
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("No." + no + "准备完毕,等待发令枪");
try {
begin.await();
System.out.println("No." + no + "开始跑步了");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("No." + no + "跑到终点了");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
end.countDown();
}
}
};
service.submit(runnable);
}
//裁判员检查发令枪...
Thread.sleep(5000);
System.out.println("发令枪响,比赛开始!");
begin.countDown();
end.await();
System.out.println("所有人到达终点,比赛结束");
}
}
信号量使用流程
信号量主要方法介绍
信号量特殊用法
注意点
作用
signalAll()和signal()的区别
Condition注意点
/**
* 描述: 演示Condition的基本用法
*/
public class ConditionDemo1 {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
void method1() throws InterruptedException {
lock.lock();
try{
System.out.println("条件不满足,开始await");
condition.await();
System.out.println("条件满足了,开始执行后续的任务");
}finally {
lock.unlock();
}
}
void method2() {
lock.lock();
try{
System.out.println("准备工作完成,唤醒其他的线程");
condition.signal();
}finally {
lock.unlock();
}
}
public static void main(String[] args) throws InterruptedException {
ConditionDemo1 conditionDemo1 = new ConditionDemo1();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
conditionDemo1.method2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
conditionDemo1.method1();
}
}
/**
* 描述: 演示用Condition实现生产者消费者模式
*/
public class ConditionDemo2 {
private int queueSize = 10;
private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public static void main(String[] args) {
ConditionDemo2 conditionDemo2 = new ConditionDemo2();
Producer producer = conditionDemo2.new Producer();
Consumer consumer = conditionDemo2.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread {
@Override
public void run() {
consume();
}
private void consume() {
while (true) {
lock.lock();
try {
while (queue.size() == 0) {
System.out.println("队列空,等待数据");
try {
notEmpty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.poll();
notFull.signalAll();
System.out.println("从队列里取走了一个数据,队列剩余" + queue.size() + "个元素");
} finally {
lock.unlock();
}
}
}
}
class Producer extends Thread {
@Override
public void run() {
produce();
}
private void produce() {
while (true) {
lock.lock();
try {
while (queue.size() == queueSize) {
System.out.println("队列满,等待有空余");
try {
notFull.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.offer(1);
notEmpty.signalAll();
System.out.println("向队列插入了一个元素,队列剩余空间" + (queueSize - queue.size()));
} finally {
lock.unlock();
}
}
}
}
}
CyclicBarrier和CountDownLatch的区别
Semaphore和AQS的关系
AQS的比喻
如果没有AQS
同步状态的原子性管理
线程的阻塞与解除阻塞
队列的管理
AQS最核心的就是三大部分:
state状态1
state状态2
控制线程抢锁和配合的FIFO队列
AQS 用法
AQS在CountDownLatch的应用
AQS在Semaphore的应用
AQS在ReentrantLock的应用
/**
* 描述: 自己用AQS实现一个简单的线程协作器
*/
public class OneShotLatch {
private final Sync sync = new Sync();
public void signal() {
sync.releaseShared(0);
}
public void await() {
sync.acquireShared(0);
}
private class Sync extends AbstractQueuedSynchronizer {
@Override
protected int tryAcquireShared(int arg) {
return (getState() == 1) ? 1 : -1;
}
@Override
protected boolean tryReleaseShared(int arg) {
setState(1);
return true;
}
}
public static void main(String[] args) throws InterruptedException {
OneShotLatch oneShotLatch = new OneShotLatch();
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"尝试获取latch,获取失败那就等待");
oneShotLatch.await();
System.out.println("开闸放行"+Thread.currentThread().getName()+"继续运行");
}
}).start();
}
Thread.sleep(5000);
oneShotLatch.signal();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"尝试获取latch,获取失败那就等待");
oneShotLatch.await();
System.out.println("开闸放行"+Thread.currentThread().getName()+"继续运行");
}
}).start();
}
}
Callable和Future的关系
get()方法:获取结果
get方法的行为取决于Callable任务的状态,只有以下这5种情况:
get(long timeout,TimeUnit unit):有超时的获取
cancel方法
isDone() 方法
isCancelled()方法
Future代码演示
cancel方法:取消任务的执行
Future.cancel(true)适用于:
任务能够处理interrupt
Future.cancel(false) 仅用于避免启动尚未启动的任务,适用于:
1.未能处理interrupt的任务
2.不清楚任务是否支持取消
3.需要等待已经开始的任务执行完成
/**
* 描述: 演示一个Future的使用方法
*/
public class OneFuture {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
Future<Integer> future = service.submit(new CallableTask());
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
static class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return new Random().nextInt();
}
}
}
/**
* 描述: 演示一个Future的使用方法,lambda形式
*/
public class OneFutureLambda {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
// Callable callable = new Callable() {
// @Override
// public Integer call() throws Exception {
// Thread.sleep(3000);
// return new Random().nextInt();
// }
// };
Callable<Integer> callable = () -> {
Thread.sleep(3000);
return new Random().nextInt();
};
Future<Integer> future = service.submit(callable);
try {
System.out.println(future.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
}
/**
* 描述: 演示批量提交任务时,用List来批量接收结果
*/
public class MultiFutures {
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(20);
ArrayList<Future> futures = new ArrayList<>();
for (int i = 0; i < 20; i++) {
Future<Integer> future = service.submit(new CallableTask());
futures.add(future);
}
Thread.sleep(5000);
for (int i = 0; i < 20; i++) {
Future<Integer> future = futures.get(i);
try {
Integer integer = future.get();
System.out.println(integer);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
static class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return new Random().nextInt();
}
}
}
/**
* 描述: 演示get方法过程中抛出异常,for循环为了演示抛出Exception的时机:
* 并不是说一产生异常就抛出,直到我们get执行时,才会抛出。
*/
public class GetException {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(20);
Future<Integer> future = service.submit(new CallableTask());
try {
for (int i = 0; i < 5; i++) {
System.out.println(i);
Thread.sleep(500);
}
System.out.println(future.isDone());
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("InterruptedException异常");
} catch (ExecutionException e) {
e.printStackTrace();
System.out.println("ExecutionException异常");
}
}
static class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
throw new IllegalArgumentException("Callable抛出异常");
}
}
}
**
* 描述: 演示get的超时方法,需要注意超时后处理,调用future.cancel()。演
* 示cancel传入true和false的区别,代表是否中断正在执行的任务。
*/
public class Timeout {
private static final Ad DEFAULT_AD = new Ad("无网络时候的默认广告");
private static final ExecutorService exec = Executors.newFixedThreadPool(10);
static class Ad {
String name;
public Ad(String name) {
this.name = name;
}
@Override
public String toString() {
return "Ad{" +
"name='" + name + '\'' +
'}';
}
}
static class FetchAdTask implements Callable<Ad> {
@Override
public Ad call() throws Exception {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("sleep期间被中断了");
return new Ad("被中断时候的默认广告");
}
return new Ad("旅游订票哪家强?找某程");
}
}
public void printAd() {
Future<Ad> f = exec.submit(new FetchAdTask());
Ad ad;
try {
ad = f.get(2000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
ad = new Ad("被中断时候的默认广告");
} catch (ExecutionException e) {
ad = new Ad("异常时候的默认广告");
} catch (TimeoutException e) {
ad = new Ad("超时时候的默认广告");
System.out.println("超时,未获取到广告");
boolean cancel = f.cancel(true);
System.out.println("cancel的结果:" + cancel);
}
exec.shutdown();
System.out.println(ad);
}
public static void main(String[] args) {
Timeout timeout = new Timeout();
timeout.printAd();
}
}
/**
* 描述: 演示FutureTask的用法
*/
public class FutureTaskDemo {
public static void main(String[] args) {
Task task = new Task();
FutureTask<Integer> integerFutureTask = new FutureTask<>(task);
// new Thread(integerFutureTask).start();
ExecutorService service = Executors.newCachedThreadPool();
service.submit(integerFutureTask);
try {
System.out.println("task运行结果:"+integerFutureTask.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程正在计算");
Thread.sleep(3000);
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
}