一.进程是怎么在操作系统中运行的?
1.同步机制应遵循的规则:
1.1空闲让进
当无进程处于临界区时,表明临界资源处于空闲状态,应允许一个请求进入临界区的进程立即进入自己的临界区,
以有效的利用临界资源。
1.2忙则等待
保证互斥访问临界资源。
1.3有限等待
对要求访问临界资源的进程,应保证在有限时间内进入自己的临界区,以免陷入"死等"状态。
1.4让权等待
当进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入"忙等"。
二.java线程池中各参数的作用。
1.workQueue
SynchronousQueue:没有容量,直接提交线程池执行。newCachedThreadPool线程池即使用的该队列。
ArrayBlockingQueue:有界队列,如果该队列已满,则将触发拒绝策略。
LinkedBlockingQueue:一个由链表组成的有界队列,此队列的默认长度为Integer.MAX_VALUE。
LinkedTransferQueue:一个由链表组成的无界阻塞队列。
PriorityBlockingQueue:该队列可以根据任务自身的优先级顺序执行。
2.拒绝策略policy
AbortPolicy:直接抛出异常,阻止系统正常工作。
CallerRunsPolicy:在调用者线程中运行当前被丢弃的任务。
DiscardOldestPolicy:丢弃最老的一个请求,也就是即将执行的一个任务,并尝试再次提交当前任务。
DiscardPolicy:该策略默默丢弃无法处理的任务,不予任何处理。
如以上策略仍然无法满足实际应用的需要,可以自己扩展RejectedExecutionHandler。
3.设置线程池数量
Ncpu=CPU的数量
Ucpu=目标cpu的使用率,0<=Ucpu<=1
W/C=等待时间与计算时间的比率
Nthreads=Ncpu * Ucpu * (1+W/C)
一般情况下io密集型意味着等待时间W会加大,所以核心线程数应该对Ncpu加倍,比如2*Ncpu;
cpu密集型意味着计算时间C会变大,那么按照公式算出来的核心线程数,就应该接近Ncpu。
Ncpu的个数可以通过以下代码获得:
Runtime.getRuntime().availableProcessors();
4.最大线程数-maximumPoolSize
线程池里面的线程数不能超过最大线程数。即最大线程里面包含核心线程。
三.锁
1.共享锁
是指锁可被多个线程所持有的,例如ReentrantReadWriteLock,写锁优先级高于读锁,且写时阻塞。但是,可有多个线程持有读锁。
2.可重入锁
是指同一个线程可以多次获取锁,如下图所示methodA内部调用methodB,如果synchronized不可重入,则会死锁。所以synchronized和ReentrantLock都是可重入锁。
public synchronized void mehtodA() throws Exception{
// Do some magic tings
mehtodB();
}
public synchronized void mehtodB() throws Exception{
// Do some magic tings
}
3.乐观锁
比如cas,版本号机制。
乐观是指当你执行某项计算时,实际上没有使用互斥,但是在执行计算完成,并且你准备更新这个Atomic对象或者LongAdder对象时,你需要使用一个称为compareAndSet()的方法将旧值和新值一起提交给这个方法,如果旧值与它在Atomic对象中(即内存中的值)发现的值不一致,那么这个操作失败——这意味着某个其他任务已经于此操作执行期间修改了这个对象。- d
还有jpa中的@Version注解也是一种乐观锁。
参考资料:
a.《计算机操作系统》——汤子瀛 哲凤屏 汤小丹——Page 41
b.《实战Java高并发程序设计(第2版)》——葛一鸣——Page 110、112、113、119
c. https://mp.weixin.qq.com/s/l6ee7k0n7CCVFgBS4tI2kQ
d. Think in java——Page 762、760