Java锁

公平锁/非公平锁

概念:所谓公平锁,就是多个线程按照申请锁的顺序来获取锁,类似排队,先到先得。而非公平锁,则是多个线程抢夺锁,会导致优先级反转饥饿现象

区别:公平锁在获取锁时先查看此锁维护的等待队列为空或者当前线程是等待队列的队首,则直接占有锁,否则插入到等待队列,FIFO原则。非公平锁比较粗鲁,上来直接先尝试占有锁,失败则采用公平锁方式。非公平锁的优点是吞吐量比公平锁更大。

synchronizedjuc.ReentrantLock默认都是非公平锁ReentrantLock在构造的时候传入true则是公平锁

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }

可重入锁

ReetrantLock /Syn 都是可重入锁

可重入锁又叫递归锁,指的同一个线程在外层方法获得锁时,进入内层方法会自动获取锁。也就是说,线程可以进入任何一个它已经拥有锁的同步代码块。比如get方法里面有set方法,两个方法都有同一把锁,得到了get的锁,就自动得到了set的锁。

就像有了家门的锁,厕所、书房、厨房就为你敞开了一样。可重入锁可以避免死锁的问题。

public class ReentrantLockDemo {
    public static void main(String[] args) {
        Phone phone = new Phone();
        new Thread(() -> {
            phone.sendSMS(); //进入sendSMS同步方法
        },"t1").start();
    }
}
class Phone{
    public synchronized void sendSMS(){
        System.out.println(Thread.currentThread().getName()+"\t invoke sendSMS()");
        sendEmail(); //调用sendEmail同步方法
    }
    public synchronized void sendEmail(){
        System.out.println(Thread.currentThread().getName()+"\t invoke sendEmail()");
    }
}

结果:t1     invoke sendSMS()
           t1     invoke sendEmail()

自旋锁

所谓自旋锁,就是尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取。自己一直循环获取,就像“自旋”一样。这样的好处是减少线程切换的上下文开销,缺点是会消耗CPU。CAS底层的getAndAddInt就是自旋锁思想。

public class SpinLockDeno {
    //原子引用线程
    AtomicReference<Thread> atomicReference = new AtomicReference<>();
    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName()+"\t coming in");
        //被线程占有则进入循环,等待释放
        while(!atomicReference.compareAndSet(null,thread))
        {

        }
    }
    public void myUnlock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
        System.out.println(Thread.currentThread().getName()+"\t invoke myUnlock");
    }
    public static void main(String[] args) {
        SpinLockDeno spinLockDeno = new SpinLockDeno();
        new Thread(() -> {
            spinLockDeno.myLock();
            //等待5秒,让B线程也进入myLock中,等待A线程释放
            try{ TimeUnit.SECONDS.sleep(5); }catch (Exception e){ e.printStackTrace(); }
            spinLockDeno.myUnlock();
        },"AA").start();
        //暂停1秒
        try{ TimeUnit.SECONDS.sleep(1); }catch (Exception e){ e.printStackTrace(); }
        new Thread(() -> {
            spinLockDeno.myLock();
            spinLockDeno.myUnlock();
        },"BB").start();
    }
}

AA     coming in
BB     coming in
AA     invoke myUnlock
BB     invoke myUnlock

AA线程先进入 compareAndSet 方法,此时参数不为null,BB线程在while循环中等待,当AA线程释放锁时,参数为null,BB线程才可以进入。BB线程在等待空闲时就是进行自旋操作。

读写锁

读锁共享的写锁独占的juc.ReentrantLocksynchronized都是独占锁,独占锁就是一个锁只能被一个线程所持有。有的时候,需要读写分离,那么就要引入读写锁,即juc.ReentrantReadWriteLock

比如缓存,就需要读写锁来控制。缓存就是一个键值对,以下Demo模拟了缓存的读写操作,读操作get方法使用了ReentrantReadWriteLock.ReadLock(),写操作put方法使用了ReentrantReadWriteLock.WriteLock()。这样避免了写独占,实现了读共享。

class MyCache{
    private volatile Map<String,Object> map = new HashMap<>();
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    ReentrantLock lock = new ReentrantLock();

    public void put(String key,Object value){
        rwLock.writeLock().lock();
        try{
            System.out.println(Thread.currentThread().getName()+"\t 正在写入:"+key);
            try{ TimeUnit.SECONDS.sleep(1); }catch (Exception e){ e.printStackTrace(); }
            map.put(key,value);
            System.out.println(Thread.currentThread().getName()+"\t 写入完成");
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            rwLock.writeLock().unlock();
        }
    }
    public void get(String key){
        rwLock.readLock().lock();
        try{
            System.out.println(Thread.currentThread().getName()+"\t 正在读取");
            try{ TimeUnit.SECONDS.sleep(1); }catch (Exception e){ e.printStackTrace(); }
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName()+"\t 读取完成:"+result);
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            rwLock.readLock().unlock();
        }
    }
}

public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();
        for (int i = 1; i <=5 ; i++) {
            final int tempInt = i;
            new Thread(() -> {
                myCache.put(tempInt+"",tempInt+"");
            }, String.valueOf(i)).start();
        }
        for (int i = 1; i <=5 ; i++) {
            final int tempInt = i;
            new Thread(() -> {
                myCache.get(tempInt+"");
            }, String.valueOf(i)).start();
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值