java并发编程-学习
Posted linzx2015
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java并发编程-学习相关的知识,希望对你有一定的参考价值。
1.竞态条件:多个线程共享相同的内存地址空间,并且并发执行下发生访问或修改其他线程正在使用的变量,而导致结果不一致。
2.活跃性:某件正确的事情最终发生,但不够好,因此需要解决性能问题
3.在多线程中,当线程调度器临时挂起活跃线程并转而运行另外一个线程时,就会产生频繁的上下文切换(Context Switch),将会带来极大的开销,
保存和恢复执行上下文,丢失局部性,并且cpu时间将更多地花在线程调度而不是线程运行上。
线程安全性:(核心:正确性,当多个线程访问某个类时,这个类始终都能表现出正确的行为)
1.编写线程安全的代码,核心在于对状态访问操作进行管理,特别是共享的和可变的状态的访问。
2.同步机制:synchronized:独占的加锁方式
3.多线程访问同一个可变的状态变量没有使用合适的同步,会出现错误,修复方式:
a.不在线程之间共享该状态变量
b.将状态变量改为不可变的变量
c.在访问变量时使用同步
4.竞态条件:由于不恰当的执行时序而出现不正确的结果(最常出现的类型:先检查后执行)
5.实际情况中,应尽可能地使用现有的线程安全对象(如AtomicLong)来管理类的状态,以确保准确性
6.内置锁:每个java对象都可用作一个实现同步的锁;互斥体(互斥锁)最多只有一个线程能持有这种锁
a.同步代码块(synchronized):一作为锁的对象引用,一个作为由这个锁保护的代码块;
b.重入性:如果某个线程试图获得一个已经由它持有的锁,那么这个请求就会成功
7.常见的加锁约定(如Vector):将所有的可变状态都封装在对象内部,并通过对象的内置锁对所有访问可变状态的代码路径
进行同步,使得在该对象上不会发生并发访问
8.重排序:a、编译器和处理器不会改变存在数据依赖性关系的两个操作的执行顺序
b、单线程下,不能改变数据的执行结果
9.最低安全性:当线程在没有同步的情况下读取变量时,可能会得到一个失效值,但至少这个是由之前某个线程设置的值,而不是随机值。
10.加锁与可见性:为确保所有线程都能看到共享变量的最新之值,所有执行读操作或写操作的线程都必须在同一个锁上同步。
11.Volatile变量:当把变量声明为volatile类型后,编译器与运行时都会注意到该变量时共享的,因此不会将该变量的操作与其他内存操作
一起重排序;也不会被缓存在寄存器或者处理器不可见的地方,因此总返回最新写入的值。
缺点:不足以确保递增操作的原子性(count++),加锁能既可确保可见性又可以确保原子性,而volaitile只能确保可见性
12.调试:对于服务器应用程序,无论在开发阶段还是测试阶段,当启动jvm时一定要指定-server命令行选项。server模式的JVM比clienr模式的
进行了更多的优化
13.发布:是一个对象能在当前作用域之外的代码中使用,最简单的方法(使用静态修饰)
逸出:某个不应该被发布的对象却被发布了
14.线程封闭(JDBC的Connection对象):访问共享数据时,需要同步,而避免使用同步就是不共享数据或者单线程内访问数据
Ad-hoc线程封闭(不推荐):维护线程封闭性的职责完全由程序实现来承担;线程封闭技术场景:通常是因为要将某个特定的子系统实现为一个单线程子系统
15.栈封闭:只能通过局部变量才能访问对象,并且确保对象不会逸出
16.ThreadLocal类(常作为上下文等使用):是线程中的某个值与保存值的对象关联,但在java5.0中,已被每次调用时分配一个新的缓冲区替代。
17.不可变对象(一定是线程安全的):某个对象在被创建后其状态就不能再被修改 @Immutable修饰
条件:1.对象创建以后其状态就不能修改 2.对象的所有域都是final类型 3.对象是正确创建的(在对象的创建期间,this引用没有逸出)
18.Final:final类型的域是不能修改的,但如果修饰的是对象的引用,则引用的对象是可修改的。
编程习惯:除非需要某个域是可变的,否则应将其声明为final域
19.要安全发布一个对象,且对象的引用及对象的状态必须同时对其他线程可见。
java中常见的:Hashtable、synchronizedMap或concurrentHashMap等
1.在静态初始化函数中初始化一个对象引用 2.将对象的引用保存到volatile类型的域或者AtomicReferance对象中
3.将对象的引用保存到某个正确构造对象的final类型域中 4.将对象的引用保存到一个由锁保护的域中
20.事实不可变对:对象从技术上来看是可变的,但其状态在发布后不会再改变
21.并发程序中使用和共享对象策略:
a.线程封闭:线程封闭的独享只能由一个线程拥有,对象被封闭在该线程中,并且只能由这个线程修改
b.只读共享:在没有额外同步的情况下,共享的只读对象可由多个线程并发访问,但任何线程都不能修改;
共享的只读对象包括不可变对象和事实不可变对象
c.线程安全共享:线程安全的对象在其内部实现同步,多线程可通过对象的公有接口来进行访问而不需要进一步的同步
d.保护对象:被保护的对象只能通过持有特定的锁来访问
四、对象的组合:
1.设计线程安全的类基本要素:
a.找出构成对象状态的所有变量 b.找出约束状态变量的不变性条件(final类型的域使用得越多,越能简化对象可能状态的分析过程) c.建立对象状态的并发访问管理策略
2.状态的所有权:
3.实例封闭:对象被封闭在类的实例中或者一个作用域中或者线程内,而不是被多个线程之间共享
4.监听器模式:
·········
5.如果一个状态变量是线程安全的,并且没有任何不变性条件来约束它的值,在变量的操作上也不存在任何不允许的状态转换,则可以
安全地发布该变量
6.Collections.synchronizedList等方法可对集合进行加锁操作
五、基础构建模块:
1.同步容器类:Vector和Hashtable 2.ConcurrentModificationException:容器在迭代过程中被修改时(当对象之间从容器中删除而不是通过Iterator.remove来删除),会抛出此异常
3.隐藏迭代器: 编译器会把字符串的连接操作转化为调用StringBuilder.append(Object),而此方法又会调用容器的toString(),因此可能
出现ConcurrentModificationException
4.HashMap.get或List.contains可能包含的工作:当遍历散列桶或链表查找某个特定的对象时,必须在许多元素上调用equals
ConcurrentHashMap:不再是将每个方法都在同一个锁上同步并使得每次只能有一个线程访问;而是使用分段锁这种更细粒度的加锁机制
来实现更大程度的共享。不再会抛出ConcurrentModificationException
5.中断是一种协作机制,一个线程不能强制其他线程停止正在执行的操作而去执行其他的操作。
当调用方法抛出InterruptedException时处理策略:
a.传递InterruptedException,不捕获该异常,或者捕获该异常,再在执行某种简单的清理工作后再次抛出该异常。
b.恢复中断,有时不能抛出InterruptedException,如Runnable中必须捕获InterruptedException,并通过调用当前线程上的interrupt方法恢复中断状态。
a.闭锁:可以延迟线程的进度直到其到达终止状态,一次性对象,一旦进入终止状态,就不能被重置。
CountDownLatch:可以使一个或多个线程等待一组事件发生。 countDown():递减计数器,表示有一个事件已经发生
await():如果计数器的值非零,await会一直阻塞直到计数器为零,或者等待中的线程中断,或者等待超时。当计数器达到零,表示所有需要等待的事件都已经发生
b.FutureTask:表示一种抽象的可生成结果的计算(通过Callable来实现),可处于等待执行、正在执行、运行完成三种状态
future.get行为取决于任务的状态;如果任务已完成,则立即返回结果;否则将阻塞直到任务进入完成状态。
Callable表示的任务可抛出受检查或未受检查的异常,并且任何代码都可能抛出一个Error,最后被封装到ExecutionException中,并在Future.get中重新抛出。
c.计数信号量(Semaphore):用于控制同时访问某个特定资源的操作数量,或者执行某个指定操作的数量。场景:资源池、容器的边界
d.栅栏:类似于闭锁,能阻塞一组线程直到某个事件发生
与闭锁区别:所有线程必须同时到达栅栏位置,才能继续执行;闭锁用于等待事件,而栅栏用于等待其他线程。
六 任务执行:
1.为提高处理效率,无限创建线程的不足:
a.线程生命周期的开销非常高 b.资源消耗,活跃的线程会消耗系统资源,尤其是内存
c.稳定性,可创建线程的数量上存在一个限制(如JVM的启动参数、Thread构造函数中请求的栈大小、底层系统对线程的限制等,破坏限制可能oom)
2.Executor框架:JVM只有在所有线程(非守护)全部终止后才会退出,如果无法正确关闭Executor,则JVM将无法结束
a.基于生产者-消费者模式,提交任务的操作相当于生产者,执行任务的线程相当于消费者
b.线程池:重用现有的线程而不是创建新线程,可在处理多个请求时分摊在线程创建和销毁过程中产生的巨大开销。
c.newFixedThreadPool():返回固定数量的线程池,该方法的线程数始终不变,当有任务提交,如果线程池中空闲,则立即执行,
若没有则会被暂缓在一个任务队列中等待有空闲的线程再执行
newSingleThreadExecutor ():创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中
newCachedThreadPool():可根据实际情况调整线程个数的线程池,不限制最大线程数量,若有任务则创建线程,
若无任务则不创建线程。如果无任务则线程在60s后自动回收(默认),超过处理需求,则回收空闲的线程
newScheduledThreadPool():返回一个SchededExecutorService对象,但线程池可指定线程的数量。
2.Timer负责管理延迟任务及周期任务,但在执行定时任务时只会创建一个线程,如果某个任务的执行时间过长,则会破坏其他TimeTask的定时精确性;如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行(线程泄露);Timer执行周期任务时依赖系统时间;而ScheduledExecutorService基于时间的延迟,不会由于系统时间的改变发生执行变化。
另外如果要构建自己的调度服务,可以使用DelayQueue,只有某个元素逾期后,才能从DelayQueue中执行take操作。 3.Executor框架(生命周期:创建、提交、开始和完成)使用Runnable作为任务的基本形式,但Runnable不能返回一个值或抛出一个受检查异常;因此更推荐使用Callable;
Future表示一个任务的声明周期,并提供判断是否已经完成和获取任务的结果等方法;get方法取决于任务的状态(尚未开始、正在运行、已完成);如果任务已完成,则立即返回或抛出一个Exception;如果任务没完成,则会阻塞并直到任务完成
4.ExecutorService中所有submit方法都将返回一个Future,从而将一个Runnable或Callable提交给Executor,得到Future来获得任务的执行结果或取消任务
5.CompletionService将Executor和BlockingQueue的功能融合在一起,可将Callable任务提交给其执行,再使用take和poll来获得已完成的结果。
ExecutorCompletionService实现CompletionService,在构造函数中创建一个BlockingQueue来保存结果,当计算完调用Future-Task的done方法;当提交某个任务时,该任务将首先包装成一个QueuingFuture,再改写子类的done方法,结果放入BlockingQueue中。
6.中断:不会真正地中断一个正在运行的线程,而是发起中断请求,由线程在下一个合适的时刻中断自己。
处理中断:除非需要屏蔽中断,否则应抛出InterruptedException或者通过再次调用interrupt来恢复中断状态
a.传递异常 b.恢复中断状态,从而使调用栈中的上层代码能进行处理
中断策略:某种形式的线程级或者服务级取消操作:应尽快退出,在必要时清理,通知某个所有者线程已经退出。
编程习惯:出现异常时,取消那些不再需要结果的任务
7.处理不可中断的阻塞:
a.Java.io包中的同步Socket I/O 最常见的阻塞I/O形式就是对套接字进行读取和写入,对InputStream和OutputStream关闭底层套接字来抛出一个SocketException
b.I/O 当中断一个正在InterruptibleChannel(大多数chanel都实现了该标准)上等待的线程,将使其及该链路上的线程
抛出CloseByInterruptException
当关闭一个InterriotibleChannel是将导致链路操作上阻塞的线程都跑出AsynchrousCloseException
c.Selector的异步I/O 如果一个线程在调用Selector.select(nio包)方法时阻塞了,则调用close或wakeup会使线程抛出CloseSelectorException并返回 d.获取某个锁 Lock类中提供了lockInterruptibly来允许在等待一个锁的同时仍能响应中断
e.停止基于线程的服务:除非拥有某个线程,否则不能对该线程进行操控,一般由线程池来关闭。
f.关闭ExecutorService:shutdown正常关闭和shutdownNow强行关闭(关闭当前正在执行的任务,再返回所有尚未启动的任务清单)
h."毒丸"对象:放在一个队列上的对象,当得到这对象时,立即停止;场景:只有在生产者和消费者的数量都已知的情况下,才可使用"毒丸"对象
i.通过execute提交的任务,才能将抛出的异常交给未捕获异常处理器;而通过submit提交的任务由于抛出了异常而结束,则该异常将被Future.get封装在ExecutionException重新抛出(被认为是返回状态的一部分)。
j.JVM关闭:正常关闭中,JVM先调用所有已注册的关闭钩子(Shutdown Hook:通过Runtime.addShutdownHook注册的但尚未开始的线程),
当所有的关闭钩子都执行结束时,如果runFinalizersOnExit为true,则JVM运行终结器,然后再停止。
k.线程:普通线程和守护线程;差异:当一个线程退出时,jvm会检查其他正在运行的线程,如果都是守护线程,则会正常退出;当jvm停止时,所有仍然存在的守护线程都将被抛弃(既不会执行finally,也不会执行回卷栈,而是直接退出)
七.线程池的使用:
1.只有当任务都是同类型的并且相互独立时,线程池的性能才能达到最佳。如果将运行时间较长的任务与较短的任务混合在一起,除非线程池很大,否则将可能造成“拥塞”;如果提交的任务依赖于其他任务,除非线程池无限大,否则将可能造成死锁。、
2.线程饥饿死锁:线程池中的任务需要无限期地等待一些必须由池中其他任务才能提供的资源或条件才能继续执行,那么除非线程池足够大,否则就会发生线程饥饿死锁
3.缓解长时间任务造成的影响,即限定任务等待资源的时间,而不无限制等待(限时版本和无限时版本);如Thread.join、BlockingQueue.put
、CountDownLatch.await及Selector.select等
4.设置线程池大小,参考Runtime.availableProcessors来动态计算;
对于计算密集型的任务,在拥有N个处理器的系统,线程池的大小为N+1时,通常能实现最优的利用率。
5.常见线程池:ThreadPoolExecutor创建初期,线程并不会立即启动,而是等到有任务提交时才启动,除非调用prestartAllCoreThreads;newCacheThreadPool(SynchronousQueue)将线程池的最大大小设置为Integer.MAX_VALUE,基本大小为0,此线程池理论上可无限扩展,且当需求降低时会自动收缩。
6.ThreadPoolExecutor允许提供一个BlockingQueue来保存等待执行的任务,基本的任务排队方式:无界队列(LinkedBlockingQueue)、有界队列(ArrayBlockingQueue)和同步移交(SynchronousQueue不是一个真正的队列,而是一种在线程之间进行移交的机制)
SynchronousQueue(只有当线程池是无界或可拒绝任务时有价值)不是一个真正的队列,而是一种在线程之间进行移交的机制,可以用来避免任务排队及直接将任务从生产者移交给工作线程,要将元素放入SynchronousQueue中,必须由另一个线程正在等待接收该元素。
PriorityBlockingQueue按优先级来安排任务,优先级通过自然排序或Comparator来定义
7.饱和策略:ThreadPoolExecutor可通过setRejectedExecutionHandler来修改,如果某个任务被提交到一个已关闭的Executor中,也会用到饱和策略。常见的饱和策略:
a.AbortPolicy(默认):会抛出未检查的RejectedExecutionException,调用者可捕获该异常,再处理
b.CallerRunsPolicy:既不抛弃任务也不抛出异常,而是将任务回退到调用者,从而降低新任务流量
c.DiscardPolicy:当新提交的任务无法保存到队列中等待执行时,会被悄悄抛弃
d.DiscardOldestPolicy:抛弃下一个将被执行的任务,然后尝试重新提交新的任务(如果工作队列是个优先队列,则会抛弃优先级最高的任务,因此不要和优先级队列一起使用)
8.线程工厂:每当线程池需要创建一个新线程,都是通过线程工厂方法完成,ThreadFactory.newThread(Runnable r);
9.扩展ThreadPoolExecutor:通过改写beforeExecute、afterExecute和terminated;无论任务时从run中正常返回还是抛出一个异常返回,afterExecute都会被调用(如果任务完成后带有一个Error,则不会调用afterExecute);beforeExecute同理
10.避免活跃性危险:
1.死锁(高负载下易发生):两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去
锁顺序死锁(LeftRightDeadLock):两个线程试图以不同的顺序来获得相同的锁。改用相同顺序来获取锁。
制定锁顺序时,可使用System.identityHashCode方法获取hashCode来决定。
2.活跃性问题:如果在持有锁时调用某个外部方法,而外部方法还可能会获取其他的锁,或阻塞时间过长,导致其他线程无法及时获得当前被持有的锁,就可能产生死锁
3.开放调用:在调用某个方法时不需要持有锁
4.支持定时的锁:Lock的tryLock可指定一个超时时限,在等待超过该时间后会返回一个失败信息
5.尽量避免使用线程优先级,因为会增加平台依赖性,并可能导致活跃性(死锁)问题(大多数并发程序中,都可直接使用默认的线程优先级)
6.另一种活跃性问题:活锁(通常发生于处理事务消息的程序),不会阻塞线程,但也不能继续执行,因为线程将不断重复执行相同的操作,而且总会失败;避免:等待随机长度的时间和回退
11.性能与可伸缩性:
1.性能:服务时间、延迟时间、吞吐率等来衡量-->perfbar可给出CPU的忙碌程序信息
可伸缩性:当增加计算资源时(如CPU、内存)程序的吞吐量或者处理能力相应地增加
2.线程上下文切换:如果可运行的线程大于CPU的数量,则操作系统最终会将某个正在运行的线程调度出来,使得其他线程能使用CPU,这操作将导致一次上下文切换:保存当前运行线程的执行上下文,并将新调度进来的线程的执行上下文设置为当前上下文
大多数通用的处理器上,上下文切换开销相当于5000-1000个时钟周期(几微秒)
3.内存同步:在synchronized和volatile提供的可见性中,内存栅栏扮演着刷新缓存,使缓存无效,刷新硬件的写缓冲及停止执行管道角色
jvm优化不必要的锁:synchronized(new Object())
逸出分析:abc被封闭在栈中且都会自动成为线程本地变量,不会发生逸出操作,编译器会去掉那3次锁获取操作
4.阻塞:如果等待事件较短,可采用自旋等待方式;如果等待时间较长,则考虑线程挂起
5.减少锁的竞争:并发程序中,对可伸缩性最主要的威胁就是独占方式的资源锁。
锁竞争原因:锁的请求频率及每次持有该锁的时间
降低锁的竞争程度:1.减少锁的持有时间 2.降低锁的请求频率 3.使用带有协调机制的独占锁
锁分解:将一个锁分解成两个锁
锁分段:将一个锁分解为多个锁;对一组对象上的锁进行分解-->concurrentHashMap;劣势:与采用单个锁来实现独占访问相比,要获取多个
锁来实现独占访问将更加困难并且开销更高
6.ReadWriteLock:如果多个读取操作都不会修改共享资源,则这些读取操作可同时访问该共享资源,但在执行写入操作时必须以独占方式来获取锁
13.显示锁:
1.Lock:提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有的加锁和解锁方法都是显式的
ReentrantLock:加锁时,必须手动释放锁,提供了与synchronized相同的互斥性和内存可见性,还为锁的不可用性问题提供了更高的灵活性
2.tryLock:轮询和定时获取锁
lockInterruptibly:能在获取锁的同时保持对中断的相应且包含于Lock锁中,无须创建其他类型的不可中断阻塞机制
内置锁中,释放锁的操作总是与获取锁的操作处于同一代码块,而不考虑控制权如何退出该代码块
非块结构的锁:ReentrantLock
越多的资源被耗费在锁的管理和调度上,则应用程度得到的资源就越少,最终导致性能下降:考虑ReentrantLock
3.公平性:
公平锁:线程按照其发出请求的顺序来获得锁,新请求的锁将被放入队列等待;在执行加锁时,会由于挂起线程和恢复线程时存在的开销而极大降低性能。tryLock仍会"插队";当持有锁的时间较长,或请求锁的平均时间间隔较长,则应使用公平锁
非公平锁:允许“插队”,当一个线程请求一个非公平锁时,如果在发出请求的同时该锁的状态变为可用,则跳过队列中等待的线程并获得该锁。
通常情况下,性能会高于公平锁的性能,原因:在恢复一个被挂起的线程与该线程真正开始运行之间存在着严重的延迟。
4.synchronized和ReentrantLock选择:如果忘记调用unlock将埋下隐患,因此当需要可定时、轮询的与可中断的锁获取操作、公平队列、以及非块结构的锁时用ReentrantLock(获取锁的操作不能与栈帧关联,而内置锁可以);否则优先用synchronized:优点:在线程转储中能给出哪些调用帧获得了哪些锁,并能检测和识别发生死锁的线程;
5.读写锁:ReadWriteLock:当读取由其保护的数据时,必须先获得读取锁;当修改时,必须先获得写入锁;
允许多个读操作同时进行,但每次只允许一个写操作;
读写交互:
ReentrantReadWriteLock:公平的锁中,等待时间最长的线程将优先获得锁;如果锁由读线程持有,另一个线程请求写入锁,则其他读线程都不能获得读取锁,直到写线程完全使用完且释放了写入锁。
非公平锁中,写线程降级为读线程是可以的,但从读线程升级为写线程则不可以(会导致死锁)
14.自定义的同步工具:
1.ArrayBlockingQueue:有界缓存:put:不能将元素放入已满的缓存中 take:不能从空缓存中获取元素
2.条件队列:使一组线程能够通过某种方式来等待特定的条件为真
当调用wait()、notify()或notifyAll等,一定要持有与条件队列相关的锁
Object.wait()会自动释放锁,并请求操作系统挂起当前线程,从而使其他线程能够获得这个锁并修改对象的状态。
如果某个功能无法通过轮询和休眠来实现,则条件队列也无法实现,但条件队列使得在表达和管理状态依赖性时更加简单和高效
3.条件谓词:使某个操作成为状态依赖操作的前提条件
条件等待:加锁、wait方法和一个条件谓词;另外锁对象与条件队列对象(即调用wait()和notify()等方法所在的对象)必须是同一个对象
4.另一种活跃性(死活、活锁等)故障:丢失的信号:线程必须等待已经为真的条件,但在开始等待之前没有检查条件谓词
5.只有当满足以下两种条件时才能使用notify而不是notifyAll:
a.所有等待线程的类型都相同:只有一个条件谓词与条件队列相关,并且每个线程在从wait返回后将执行相同的操作
b.单进单出 在条件变量上的每次通知,最多只能唤醒一个线程来执行
6.显式Condition对象:广义上的内置条件队列
Lock和Condition通常搭配使用:Lock.newCondition() Condition对Object进行了扩展,包含wait和notify,await、signal和signalAll分别对应于wait、notify、notifyAll;
7.AbstractQueuedSynchronizer:实现闭锁?是一个Java提高的底层同步工具类,用一个int类型的变量表示同步状态,并提供了一系列的CAS操作来管理这个同步状态。AQS的主要作用是为Java中的并发同步组件提供统一的底层支持,例如ReentrantLock,CountdowLatch就是基于AQS实现的,用法是通过继承AQS实现其模版方法,然后将子类作为同步组件的内部类。
8.AQS:
15.原子变量与非阻塞同步机制:
1.原子变量适用于计数器、序列发生器和统计数据收集等,同时还能比基于锁的方法提供更高的伸缩性
2.CAS包含三个操作数:需读写的内存位置V、进行比较的值A和拟写入的新值B。当且仅当V的值等于等于A时,CAS才会用原子方式将新值
来更新V的值,否则不会执行任何操作;无论位置V的值是否等于A,都将返回V原有的值
缺点:将使调用者处理竞争问题(通过重试、回退、放弃),而在锁中能自动处理竞争问题(线程在获得锁之前将一直阻塞)
3.原子变量类:12个,4组:标量类(Scalar,扩展了Number类如Integer或Long)、更新器类、数组类及复合变量类
最常用:AtomicInteger(支持算术运算)、AtomicLong(支持算术运算)、AtomicBoolean、AtomicReference 都支持CAS
4.AtomicInteger:提供了get和set方法、compareAndSet(old,new)
5.非阻塞算法:一个线程的失败或挂起不会导致其他线程失败或挂起
6.无锁算法(Lock-Free):在算法的每个步骤中都存在某个线程能够执行下去
7.ABA问题:可通过增加一个版本号来解决
8.重排序:
9.偏序关系(Happens-Before):想保证执行操作B的线程看到操作A的结果(无论A和B是否在同一个线程中执行),则A和B之间必须满足Happens-Before关系;否则JVM可对它们任意地重排序
a.程序顺序规则:如果程序中操作A在操作B之前,则线程A操作将在B操作之前执行
b.监视器锁规则:在监视器锁上的解锁操作必须在同一个监视器锁上的加锁操作之前执行
c.volatile变量规则:对volatile变量的写入操作必须在该变量的读操作之前
d.线程启动规则:在线程上对Thread.Start的调用必须在该线程执行任何操作之前执行
e.线程结束规则:线程中的任何操作都必须在其他线程检测到该线程已经结束之前执行或者Thread.join成功返回或者Thread.isAlive返回false
f.中断规则:当一个线程在另一个线程上调用interrupt时,必须在中断线程检测到interrupt之前执行(通过InterruptedException或isInterrupted和interrupted)
g:终结器规则:对象的构造函数必须在启动该对象的终结器之前执行完成
h:传递性:A->B->C,A需在C之前执行
10.全序:同步操作(锁的获取与释放)及volatile的读取与写入
11.糟糕的双重检查加锁:当在没同步的情况下读取一个共享对象,可能看到只是一个失效值;将list声明为volatile
以上是关于java并发编程-学习的主要内容,如果未能解决你的问题,请参考以下文章