公平锁和非公平锁
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(); } } } 结果如下:
原文地址:https://www.cnblogs.com/liuyi13535496566/p/12130300.html