多线程学习笔记-4
LockSupport
和wait,notify区别
wait,notify只能在同步代码中使用,lockSupport没有这个限制。lockSupport可以精准作用于某个线程,而notify只能是随机的某个线程。lockSupport不会产生死锁
中断
和stop的区别
stop是线程实例方法,调用后线程被强制终止执行,同时已经获得的锁也会被释放,可能造成数据不一致,逻辑不完整等不可预料的问题。而中断只是发送中断请求,也就是设置中断标志,至于是否真的要停止运行,取决于线程自己
isInterrupted
返回中断标志
interrupt
设置中断标志
interrupted
返回中断标志并设置为false
AQS
基于AQS实现可重入独占锁
class MyLock{
private final Sync sync=new Sync();
static class Sync extends AbstractQueuedSynchronizer {
@Override
protected boolean tryAcquire(int arg) {
if(getExclusiveOwnerThread()==Thread.currentThread()){
setState(getState()+1);
return true;
}
if (getState()==0&&compareAndSetState(0,1)){
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
@Override
protected boolean tryRelease(int arg) {
if(getExclusiveOwnerThread()!=Thread.currentThread())throw new IllegalMonitorStateException();
int c=getState()-arg;
if(c==0){
setExclusiveOwnerThread(null);
}
setState(c);
return c==0;
}
}
public void lock(){
sync.acquire(1);
}
public void unlock(){
sync.release(1);
}
}
public class Main{
public static void main(String[] args) throws InterruptedException {
MyLock myLock = new MyLock();
int[] a={0};
Thread t=new Thread(()->{
for (int i = 0; i < 10000000; i++) {
myLock.lock();
a[0]++;
myLock.unlock();
}
});
t.start();
for (int i = 0; i < 10000000; i++) {
myLock.lock();
a[0]++;
myLock.unlock();
}
t.join();
System.out.println(a[0]);
}
}
锁
乐观锁&悲观锁
乐观锁和悲观锁是数据库中引入的名词,但是在java并发包中也引入了类似思想。
悲观锁认为多线程数据处理时,数据容易被修改,进行数据处理前需要获得排它锁,如果获取失败,则说明数据正在被其他线程处理,从而会抛出异常或者等待,如果获取成功则进行处理,然后释放锁
乐观锁认为数据处理一般不会冲突,在访问数据前不加排它锁,而在数据更新提交时检查数据是否冲突,采用了类似CAS的思想
悲观锁的实现往往依靠数据库的锁机制实现,而乐观锁并不需要数据库的锁机制,一般在表中添加version字段实现,乐观锁直到提交时才锁定,所以不会产生死锁。
数据库update操作是原子的
公平锁&非公平锁
公平锁指的是多个线程竞争锁时,根据时间顺序,先来的先获得锁,而非公平锁则不一定,后来的可能先获得锁。ReentrantLock默认是非公平锁,除非必要,否则使用非公平锁,因为公平锁会性能差一些
独占锁和共享锁
独占锁保证同一时间只能有一个线程获得锁,ReentrantLock就是独占锁。而共享锁可以让多个线程在同一时间获得同一个锁,ReadWriteLock是共享锁,它同一时刻运行多个线程读操作。
独占锁是悲观锁,每次访问资源都加锁,限制了并发访问性能,因为读不会影响数据一致性。共享锁是乐观锁。
可重入锁
当一个线程获得独占锁后,再去获取该锁,如果不被阻塞,这个锁就是可重入锁。synchronized是可重入锁。可重入锁内部,维护了一个线程标识,用来标识锁被哪个线程占用,同时有一个计数器。当锁被某个线程获得时,计数器置为1,如果被该线程再次获取,计数器+1,释放时-1,如果计数器为0,则将线程标识置为null,这时阻塞的线程会被唤醒来竞争锁
自旋锁
当线程获取自旋锁失败时,不会立刻阻塞自己,不放弃cpu使用权,继续多次尝试获取(jvm默认10次),直到成功或者超过次数。可以减少上下文切换和线程调度的开销,但是会增加cpu的使用时间。

浙公网安备 33010602011771号