java 锁
Posted new 个对象()
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java 锁相关的知识,希望对你有一定的参考价值。
公平锁,非公平锁
- 公平锁:在多线程环境下,按照申请锁的顺序获得锁
- 非公平锁:在多线程环境下,不一定按照申请锁的顺序获得锁,后申请锁的线程可以优先获得锁,有可能造成优先级反转或者饥饿现象
在ReentrantLock中,可以通过构造函数中的boolean值来指定是否为公平锁,默认是非公平锁,非公平锁的优点在于吞吐量比公平锁大
Lock lock=new ReentrantLock(true);//公平锁 Lock lock1=new ReentrantLock();//非公平锁,默认为false
synchronized是一种非公平锁
可重入锁(又名递归锁)
- 线程可以进入任何一个他已经拥有的锁所同步着的代码
- ReentrantLock/synchronized就是典型的可重入锁
- 可重入锁的最大作用是避免死锁
代码验证ReentrantLock/synchronized是可重入锁
public class LockDemo2 { public static void main(String[] args) { Person person = new Person(); new Thread(() -> { person.fun1(); }, "t1").start(); new Thread(person, "t2").start(); } } class Person implements Runnable { //fun1,fun2验证synchronized是可重入锁 public synchronized void fun1() { System.out.println(Thread.currentThread().getName() + " invoked fun1"); fun2(); } public synchronized void fun2() { System.out.println(Thread.currentThread().getName() + " invoked fun2"); } @Override public void run() { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } fun3(); } //验证ReentrantLock是可重入锁 Lock lock = new ReentrantLock(); public void fun3() { try { lock.lock(); System.out.println(Thread.currentThread().getName() + " invoked fun3"); fun4(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void fun4() { try { lock.lock(); System.out.println(Thread.currentThread().getName() + " invoked fun4"); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } }
自旋锁
- 定义:尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获得锁
- 好处:减少线程上下文切换的消耗
- 坏处:循环会消耗cpu
实现一个自旋锁(CAS实现)
/** * 实现一个自旋锁 * 自旋锁好处:循环比较获取直到成功为止,没有类似wait的阻塞 * <p> * 通过CAS循环比较的方式完成自旋锁,A线程先获取锁,等待5秒,B线程进来时发现锁被使用,一直循环等待直到A释放锁 */ public class SpinLockDemo { AtomicReference<Thread> atomicReference = new AtomicReference<>(); /** * 获取锁 * 1. 判断原子引用值是否为空 * 2. 若为空,则将原子引用值设置为当前线程 * 3. 若不为空,循环等待值为空 */ public void myLock() { Thread thread = Thread.currentThread(); while (!(atomicReference.compareAndSet(null, thread))) { //自旋锁循环获取锁 System.out.println(thread.getName() + " 正在自旋循环尝试获取锁"); } System.out.println(thread.getName() + " lock"); } /** * 释放锁 * 1. 将原子引用值设置为空 */ public void myUnLock() { Thread thread = Thread.currentThread(); atomicReference.compareAndSet(thread, null); System.out.println(thread.getName() + " unlock"); } public static void main(String[] args) throws InterruptedException { SpinLockDemo spinLockDemo = new SpinLockDemo(); new Thread(() -> { spinLockDemo.myLock(); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { e.printStackTrace(); } finally { spinLockDemo.myUnLock(); } }, "A").start(); //保证上面的线程先执行 TimeUnit.SECONDS.sleep(1); new Thread(() -> { //5秒之后才能获取到锁 spinLockDemo.myLock(); spinLockDemo.myUnLock(); }, "B").start(); } }
读写锁/独占锁/共享锁
- 独占锁:该锁一次只能被一个线程持有。ReentrantLock,Synchronized都是独占锁
- 共享锁:该锁可被多个线程持有
- 对ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁。
读写锁代码示例
package com.yls.thread.lock; import java.util.HashMap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * 当一个线程对某个资源进行写操作时,应该是一个整体(原子+独占),不能被其他线程打断 */ public class ReadWriteLockDemo { public static void main(String[] args) throws InterruptedException { MyCache myCache = new MyCache(); for (int i = 0; i < 5; i++) { int finalI = i; new Thread(() -> { myCache.put(finalI, finalI); }).start(); } Thread.sleep(1); for (int i = 0; i < 5; i++) { int finalI = i; new Thread(() -> { myCache.get(finalI); }).start(); } } } //模拟缓存 class MyCache { private HashMap<Integer, Integer> hashMap = new HashMap<>(); private ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); //缓存中插入值,使用独占锁 public void put(int key, int value) { readWriteLock.writeLock().lock(); System.out.println(key + "准备放入:"); hashMap.put(key, value); System.out.println(key + "放入成功"); readWriteLock.writeLock().unlock(); } //获取数据,共享锁 public int get(int key) { readWriteLock.readLock().lock(); System.out.println("准备获取" + key + ":"); int value = hashMap.get(key); System.out.println(key + "获取成功"); readWriteLock.readLock().unlock(); return value; } public void clear() { hashMap.clear(); } }
CountDownLatch,CyclicBarrier,SemaPhore
- CountDownLatch:减为0时,执行后面的线程
- CyclicBarrier:加到某一个值时,执行指定的线程
- SemaPhore:可伸缩,对统一资源初始化一个锁池,需要使用时从锁池获取锁,使用完后释放锁还给锁池,若锁被用完,则等待其它线程释放锁
以上是关于java 锁的主要内容,如果未能解决你的问题,请参考以下文章
java中ReentrantReadWriteLock读写锁的使用
JUC并发编程 共享模式之工具 JUC CountdownLatch(倒计时锁) -- CountdownLatch应用(等待多个线程准备完毕( 可以覆盖上次的打印内)等待多个远程调用结束)(代码片段