java 常用锁

Posted 从入门到放弃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java 常用锁相关的知识,希望对你有一定的参考价值。

公平锁和非公平锁

1.公平锁,是指多个线程按照申请的顺序来获取锁,类似排队打饭,先来后到。

2.非公平锁,是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程

比先申请的线程优先获取锁,在高并发情况下,有可能会造成优先级反转或者饥饿现象。

Java ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁,非公平锁的优点在于吞吐量比公平锁大。

对于Synchronized而言,也是一种非公平锁。


 

可重入锁(也叫做递归锁)

指的是同一个线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一个线程

在外层方法获取锁的时候,在进入内层方法会自动获取锁。也即是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。

ReentrantLock/Synchronized就是一个典型的可重入锁,可重入锁的最大作用是避免死锁。

例子:

class Photo implements Runnable{

public synchronized void sendSMS() throws Exception {
System.out.println(Thread.currentThread().getName()+"\\t invoked sendSMS()");
sendEmail();
}

public synchronized void sendEmail() throws Exception {
System.out.println(Thread.currentThread().getName()+"\\t invoked sendEmail()");
}

@Override
public void run() {
get();
}
Lock lock = new ReentrantLock();

public void get() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\\t invoked sendSMS()");
set();
} finally {
lock.unlock();
}
}

public void set() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName()+"\\t invoked sendEmail()");
} finally {
lock.unlock();
}
}

}

public class ReentrantLockDemo {

public static void main(String[] args) {
Photo photo = new Photo();
new Thread(() ->{
try {
photo.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"t1").start();

new Thread(() ->{
try {
photo.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"t2").start();

try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("------------------------");
Thread t3 = new Thread(photo,"t3");
Thread t4 = new Thread(photo,"t4");
t3.start();
t4.start();
}
}

运行结果见下图

 

 


 

自旋锁(spinlock)

是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU

//unsafe.getAndAddInt(Object var1, long var2,int var4) {

  int var 5;

  do {

    var5  = this.getIntVolatile(var1,var2);

  } while(!this.compareAndSwapInt(var1,var2,var5,var5 + var4))

    return var5;

}

 

例子:

public class SpinLockDemo {
//原子引用线程
AtomicReference<Thread> atomicReference = new AtomicReference<>();

public void myLock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"\\t come in");
while (!atomicReference.compareAndSet(null,thread)){

}
}

public void myUnlock() {
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()+"\\t come out");
}

public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() ->{
spinLockDemo.myLock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
spinLockDemo.myUnlock();
},"t1").start();

try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}

new Thread(() ->{
spinLockDemo.myLock();
spinLockDemo.myUnlock();
},"t2").start();
}
}

结果如下:

 

 

 


 

独占锁:指该锁一次只能被一个线程所持有,对ReentrantLock和Synchronized而言都是独占锁

共享锁:指该锁可被多个线程所持有,对于ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁。

读锁的共享锁可保证并发读是非常高效的,读写,写读,写写的过程是互斥的

读写锁分离例子:

class MyReadWrite {
private volatile Map<String, Object> map = new HashMap<>();
//private Lock lock = new ReentrantLock();
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

public void put(String key, Object value) {
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\\t正在写入:" + key);
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\\t写入完成:" + key);
} catch (Exception e) {
e.printStackTrace();
}finally {
readWriteLock.writeLock().unlock();
}

}

public void get(String key) {

readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "\\t正在读取:");
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object result = map.get(key);
System.out.println(Thread.currentThread().getName() + "\\t读取完成:" + result);
} catch (Exception e) {
e.printStackTrace();
}finally {
readWriteLock.readLock().unlock();
}

}
}

public class ReadWriteLockDemo {
public static void main(String[] args) {
MyReadWrite myReadWrite = new MyReadWrite();
//模拟5个线程读写
for(int i = 1; i <= 5; i++) {
final int tempInt = i;
new Thread(() ->{
myReadWrite.put(tempInt+"",tempInt+"");
},String.valueOf(i)).start();
}

for(int i = 1; i <= 5; i++) {
final int tempInt = i;
new Thread(() ->{
myReadWrite.get(tempInt+"");
},String.valueOf(i)).start();
}
}
}

结果如下:

以上是关于java 常用锁的主要内容,如果未能解决你的问题,请参考以下文章

LockSupport.java 中的 FIFO 互斥代码片段

java并发线程锁技术的使用

java中ReentrantReadWriteLock读写锁的使用

常用Java程序片段

分布式锁三种解决方案

java 常用锁