@
volatile是一个关键字,用于在并发编程中修饰变量
volatile:java提供的一种弱同步机制
如何保证可见性
指令重排
多线程环境中,线程交替运行,编译器优化重排的存在,两个线程中使用的变量无法保持一致性
JMM(Java内存模型)
JMM描述的是一组规范,通过规范来定义程序中各个变量的访问方式
jmm同步规定:
不保证原子性
原子性:不可分割、完整性,即某个线程正在做某个具体业务,中间不可以被加塞或者被分割,需要整体完整,
要么同时成功,要么同时失败
使用AtomicInteger,synchronized 可以保证原子性
CompareAndSet-- 比较并设置(交换)
CAS是一条CPU并发原语,体现在sun.misc.Unsafe类中各个方法
public final boolean CompareAndSet(int expectedValue,int newValue){
return U.CompareAndSetInt(this,VALUE,expectedValue,newValue);
}
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
原子变量是一种更好的“volatile”
原子变量比锁的力度更细,量级更轻
AtomicInteger,AtomicLong,AtomicBoolean,AtomicReference
CAS的缺点
解决ABA问题
AtomicStampedReference
并发容器类(ConcurrentHashMap,CopyOnWriteArrayList)代替同步容器类(Hashtable,Vector)
CopyOnWriteArrayList的操作,读写分离
public boolean add(E e){
final ReentrantLock lock=this.lock();
lokc.lock();
try{
Object[] elements=getArray();
int len=elements.length;
Object[] newElements=Arrays.copyof(elements,len+1);
newElements[len]=e;
setArray(newElements);
return true;
}finally{
lock.unlock();
}
}
公平锁,非公平锁
ReentrantLock 默认是非公平锁,但是可以设置称为公平锁
synchronized 是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 通过两个静态内部类来实现
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可重入锁(递归锁)
递归锁是指同一个线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码
线程可以进入任何一个它已经拥有的锁所同步着的代码块
ReentrantLock/Synchronized 就是典型的可重入锁
重入锁进一步提升了加锁行为的封装性
独占锁(写锁)/共享锁(读锁)/互斥锁
独占锁:该锁一次只能被一个线程持有(ReentrantLock,Synchronized)
共享锁:该锁可以被多个线程所持有(ReentrantReadWriteLock的读锁)
自旋锁(spinlock)
尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁
锁 | synchronized | lock |
---|---|---|
原始构成 | JVM,底层使用monitor对象完成 | 具体类(java.util.concurrent.locks) |
中断 | 不可中断 | 可以中断 |
加锁公平性 | 非公平锁 | 默认是非公平,构造传入true可以实现公平 |
条件 | 无条件 | Condition |
AbstractQueuedSynchronizer
在java.util.concurrent
ReentrantLock
ReentrantReadWriteLock
SynchronousQueue
FutureTask
CountDownLatch
Semaphore
CyclicBarrier
CountDownLatch
Semaphore
可以代替Synchronize 和Lock
用于多个共享资源的互斥作用,另一个用于并发线程数的控制
CyclicBarrier
可循环(Cyclic)使用的屏障。让一组线程到达一个屏障(也可叫同步点)时被阻塞,知道最后一个线程到达屏 障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CycliBarrier的await()方法
可以设置循环使用
阻塞队列
在多线程领域:所谓阻塞,在某些情况下会挂起线程,一旦满足条件,被挂起的线程又会自动被唤醒
使用阻塞队列BlockingQueue时我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都可自行处理
阻塞队列的各种方法
方法类型 | 阻塞 | 抛出异常 | 特殊值 | 超时 |
---|---|---|---|---|
插入 | put(e) | add(e) | offer(e) | offer(e,time,unit) |
移除 | take() | remove() | poll() | poll(time,unit) |
检查 | ~ | element() | peek() | ~ |
线程池
线程池主要是用来控制线程的数量,处理过程中将任务放进队列,然后在线程创建后启动给这些任 务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来 执行
几个线程池的实现
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
corePoolSize,线程池中常驻核心线程数
在创建了线程池后,当有请求任务来之后,就会安排池中的线程去执行请求任务
当线程池的线程数达到corePoolSize后,就会把到达的任务放到缓存队列当中
maximumPoolSize,线程池能够容纳同时执行的最大线程数,必须大于等于1
keepAliveTime,多余空闲线程的存活时间
unit, 存活的时间单位 (TimeUnit.)
workQueue,任务队列,被提交但是尚未被执行的任务
threadFactory,表示生成线程池中工作线程的线程工厂,默认即可
handler,拒绝策略
RejectedExecutionHandler 拒绝策略
当线程全部占用,等待队列已经全部排满,无法接纳新的任务,需要拒绝策略机制来合理的处理这个问题
AbortPolicy 默认,直接抛出RejectedExecutionException异常阻止系统正常运行
CallerRunsPolicy,将任务回退到调用者
DiscardOldestPolicy,抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务
DiscardPolicy,直接丢弃任务
线程池创建
线程池的工作流程
配置线程池
int num_cpu=Runtime.getRuntime().availableProcessors(); // cpu的核心数
CPU核心数/(1-阻塞系数) ,阻塞系数0.8~0.9之间
原文:https://www.cnblogs.com/GeekDanny/p/12884843.html