锁是在多线程中对公共资源的控制。(java.util.concurrent.locks
)
locks包下的接口:
|---AbstractOwnableSynchronizer
|---AbstractQueuedLongSynchronizer
|---AbstractQueuedSynchronizer
|---Condition
|---Lock
|---LockSupport
|---ReadWriteLock
|---ReentrantLock
|---ReentrantReadWriteLock
乐观锁和悲观锁
乐观锁: 乐观锁会认为每次查询都不会造成更新丢失,所以每次都不会加锁,利用版本控制实现。
悲观锁: 该锁认为每次操作都会对数据造成更新丢失,每次查询时加上排它锁。
锁的类型
- 可重入锁
同一个线程中,外层函数获得锁,内层依然有该锁的代码,不受影响。
public class Hello implements Runnable{
ReentrantLock lock = new ReentrantLock();
private void worker(){
lock.lock();
System.out.println("I'm worker: " + Thread.currentThread().getId());
sayHello();
lock.unlock();
}
private void sayHello(){
lock.lock();
System.out.println("Hello Word");
lock.unlock();
}
@Override
public void run() {
worker();
}
public static void main(String[] args) {
Hello hello = new Hello();
new Thread(hello).start();
new Thread(hello).start();
new Thread(hello).start();
}
}
- 自旋锁
同一个线程两次调用lock(),会导致第二次调用lock()位置进行自旋,产生死锁。(不可重入锁)
public class SpinLock {
private AtomicReference cas = new AtomicReference();
public void lock() {
Thread current = Thread.currentThread();
// 利用CAS
while (!cas.compareAndSet(null, current)) {
// DO nothing
}
}
public void unlock() {
Thread current = Thread.currentThread();
cas.compareAndSet(current, null);
}
}
- 可中断锁
在等待获取锁的过程中可中断公平锁
public class Hello implements Runnable{
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
lock.lockInterruptibly();
System.out.println("Hello Word");
Thread.sleep(2000);
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//正常
Hello hello = new Hello();
Thread t1 = new Thread(hello);
t1.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.interrupt();
// InterruptedException异常
// Hello hello = new Hello();
// Thread t1 = new Thread(hello);
// t1.start();
// t1.interrupt();
}
}
- 公平锁
按等待获取锁的线程的等待时间进行获取,等待时间长的优先获取。(ReentrantLock默认是公平锁
)
非公平锁
public class Hello implements Runnable{
//非公平锁
ReentrantLock lock = new ReentrantLock(false);
@Override
public void run() {
try {
lock.lock();
System.out.println("Hello Word" + Thread.currentThread().getName());
Thread.sleep(2000);
lock.unlock();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Hello hello = new Hello();
Thread t1 = new Thread(hello);
t1.setName("t1");
t1.start();
Thread t2 = new Thread(hello);
t2.setName("t2");
t2.start();
Thread t3 = new Thread(hello);
t3.setName("t3");
t3.start();
}
}
- 读写锁
对资源的读写的时候拆成两个部分处理。读的时候可以多线程一起读,写的时候必须同步写。
public class Hello {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public static void main(String[] args) {
Hello hello = new Hello();
for (int i = 0; i < 3; i++) {
new Thread(() ->{
try {
// hello.read();
hello.put("Hello");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
public void put(Object object) throws InterruptedException {
lock.writeLock().lock();
System.out.println("我来写东西了++++");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " write " + object);
lock.writeLock().unlock();
}
public void read() throws InterruptedException {
lock.readLock().lock();
System.out.println("我来读东西了++++");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + " ++++++++++++");
lock.readLock().unlock();
}
}
偏向锁、轻量级锁、重量级锁
偏向锁: 它会偏向于第一个访问锁的线程,如果运行过程中,同步锁只有一个线程访问,不存在多线程竞争情况,线程不需要触发同步锁,系统会给线程加个偏向锁。
轻量级锁: 当有第二条线程加入竞争时,系统会把偏向锁升级为轻量锁。
重量级锁: 内置在java中被抽向为监视器锁(monitor)。调用成本非常高,包括系统调用引起的内核态与用户切换、线程阻塞造成的线程切换等。(Syschronized
)