目录
同步方式
常用对象
一、volatile变量
二、ThreadLocal
三、Future
四、ExecutorCompletionService
五、Thread
并发容器
一、ConcurrentHashMap
二、CopyOnWriteArrayList
线程池
一、ExecutorService
疑问
基础
一、概念
二、注意点
三、其它
java线程是通过映射到操作系统的原生线程上来实现的,所以线程调度最终还是取决于操作系统,所以阻塞唤醒线程都需要从用户态转换到内核态,可能转换的开销比代码执行的开销还大,所以是重量级的。
1、将所有可变状态封装在对象内部,并通过对象内置锁对所有访问可变状态的代码路径进行同步。
2、监视器模式:把对象所有可变状态封装起来,并由对象自己的内置锁来保护。
3、委托:如:Collections.synchronizedList(new ArrayList<>());
1、不会将变量上的操作与其它内存操作一起重排序,不会被缓存在寄存器或其它处理器不可见的地方,因此读取volatile类型变量时总放回最新写入的值。
2、只能保证可见性,通常用作简单的状态标识。
1、使线程中的某个值与保存值的对象关联起来,使用某个变量的线程都存有一份独立的副本。
2、线程值保存在Thread对象中。
1、表示任务的生命周期
2、get方法任务时阻塞的
TimeoutException:超时 InterruptedException:任务被中断 ExecutionException:任务抛出异常会被封装成ExecutionException抛出3、cancel方法
true:会中断线程停止任务。
false:如果任务还没执行会中断任务,如果任务已经开始执行会让任务执行下去。
1、作用:提交了一组计算任务,得到其中完成了的任务。
1、interrupt:中断线程(并不意味着立即停止目标线程正在进行的工作,只是传递了请求中断的消息)
2、isInterrupted:目标线程中断状态
3、静态的interrupted:清除当前线程的中断状态(清除中断状态的唯一办法),并返回它之前的值。
4、sleep()等阻塞方法,发现中断时会提前返回,并会清除中断状态,抛出InterruptedException。
5、join():线程执行join方法后,只有改线程任务执行完后,才会执行接下来的代码。
6、UncaughtExceptionHandler通过这个来捕获运行时异常,只能是execute提交的任务,submit提交的任务不行。
1、内置锁:
wait:会自动释放锁、并挂起当前线程
notify:会从等待的多个线程中选址一个来唤醒
2、显式锁(Condition):await,signal
范例:
synchronized (lock){ while(isfull()){ lock.wait(); } //dosomething }
1、分段锁
2、size和isEmpty被弱化,如:size方法返回的是一个估计值。
1、每次修改时,都会创建并重新发布一个新的容器副本。
1、shutdown:不再接受新任务,等待已经提交的任务执行完成(包括还未开始执行的任务)
2、shutdownNow:取消所有运行中的任务(不再启动队列中还没开始执行的任务),返回所有已提交但尚未开始的任务。
3、invokeAll:提交一组任务,若超时,还没有完成的任务会被取消;
1、线程栈的地址空间
2、DelayQueue
3、句柄
4、套接字
5、newtaskfor
6、关闭钩子的使用 Runtime.getRuntime().addShutdownHook()
7、通信量
1、发布:是对象能在当前作用域以外的代码中使用,如其它对象中使用该对象的引用。可能破坏安全性。
2、逸出:不该发布的对象发布了。
3、线程封闭:仅在单线程内访问数据,不需要同步。局部变量和ThreadLocal,但需要确保封闭在线程中的对象不会从线程中逸出。
4、ad-hoc线程
5、对象不可变
对象创建后状态不能修改对象所有域都是final类型对象正确创建(this引用没有逸出)6、安全发布
静态初始化函数中初始化对象引用对象引用保存在volatile类型域或AtomicReferance对象中对象引用保存在正确构造对象的final域中对象引用保存在由锁保护的域中(如:Vector)
1、执行时间较长的计算,不要持有锁。(如:网络IO)
2、非volatile类型的long和double变量,JVM允许将64位的读或写操作分解为两个32位的操作。
3、不要在this引用中使构造函数逸出。
4、每个线程都会维护两个执行栈,一个用于Java代码一个用于原生代码,JVM默认情况会生成一个复合栈,约0.5MB;
5、大量独立且同构的任务并发处理时,多线程才能使性能真正的提升。明确任务边界;分析粒度更细的并行性;
6、Socket IO或等待内置锁而阻塞,中断线程只能设置线程的中断状态,别无它用。
7、自旋等待(CPU时钟周期浪费)、休眠(低相应性)、Thread.yield(让出一定时间使另一个线程运行)、条件队列(如:wait)
1、安全性
2、原子性:原子变量提供了与volatile相同的内存语义,此外还支持原子的更新操作。
3、内存可见性
4、posix线程
5、每个对象都有一个内置锁
6、重排序:编译器操作顺序重排序,并将数值缓存在寄存器中;CPU处理器特定的缓存中。
7、不希望ExecutorService被暴露出去但不希望其被修改时,可用Executors.unconfigurableExecutorService()将其封装起来
8、notify:需要持有条件队列对象相关联的锁,JVM会从这个条件队列上等待的多个线程中选择一个来唤醒。
原因:两个线程以不同的顺序来获得相同的锁,如果按照相同的顺序来请求锁就不会有死锁的风险。
1、获得锁的同时调用外部方法要小心
2、资源死锁:A线程拿着d1连接要d2连接,B线程拿着d2连接要d1连接。
线程饥饿死锁:有界线程池与相互依赖的任务不能一起使用。饥饿指线程无法访问它所需的资源而不能继续执行时。
3、支持定时的锁
4、活锁:线程不断重复执行相同的操作,而该任务总会失败,加入随机性可防止活锁。
1、上下文切换:线程所需数据不在cpu缓存中,挂起和恢复的性能开销
2、内存同步(内存栅栏:缓存时效,不能重排序),锁粒度粗化,自旋锁,锁消除。同步会增加共享内存总线上的通信量。
3、缩小锁竞争(快进快出)
4、减少竞争的锁(分段锁)
5、Amdahl定律:减少被串行执行的部分
6、原子变量降低更新热点域的开销,细粒度原子操作,并使用处理器底层并发原语(cas)
一、ReentrantLock
可中断
可定时
可轮询
可公平
非块结构锁
对于公平锁,可轮训的trylock仍然会插队
二、读写锁ReentrantReadWriteLock
1、允许多个读操作同时进行,但是只允许一个写操作
2、可重入
3、默认是非公平的锁
4、线程持有写锁不能在不释放写锁的情况下获得读锁,反之可以。
5、如果锁由读线程持有,另一个线程请求写锁,那么其他读线程都不能获得读取锁,直到写线程释放写入锁。
三、为什么优先使用内置锁
1、他俩性能一致
2、内置锁有锁消除、锁粗化等优化措施
3、内置锁操作可以与特定栈帧关联起来,排查问题可以有更多信息。
四、Condition
Lock.newCondition
Lock和Condition相当于内置锁和条件队列的关系 ,每个Lock可以有任意数量的Condition。
继承了lock的公平性,对于公平锁,线程会依照FIFO顺序从Condition.await中释放,调用await和signal方法时也必须持有锁对象。
要使用更高级特性,如:使用公平特性、每个锁上对应多个等待线程集时。
五、AQS
用于构建锁和同步器的框架