zoukankan      html  css  js  c++  java
  • 锁的公平与非公平

      锁是给线程用的,线程拿到锁之后才能干活。当多个线程竞争一个锁时,同一个时间只能有一个线程脱颖而出持有锁,其他线程等该线程释放锁后发起下一轮竞争。那么这种竞争就存在公平性问题,如果是公平的竞争,那么这些线程就得有序来依次得到锁,这就需要线程们请求的按先来后到排队,第一个线程使用完后把锁递给第二个线程,以此类推。非公平的锁是无序的,锁在被释放那会儿刚好谁运气好碰到了就给谁。

      举个例子:线程甲、乙、丙依次请求锁,如果是公平的,那么就按这个次序来。比如乙先取到了锁,那么没有意外,等它释放后肯定是先甲后丙来取得锁;如果是非公平,甲先持有锁,乙跑过来争,发现被甲拿了就去睡觉(被挂起)了,当甲释放锁通知乙来取的过程中,刚好丙半路杀过来了,把锁拿去用了,等丙释放锁时乙刚好完全被唤醒拿到了锁。

      从上面例子可以看到,如果竞争很激烈,锁被持有时间短,那么非公平锁能充分利用时间,公平锁反而因为线程的切换浪费了时间。反之,如果线程持有锁的时间长,那么非公平锁会被频繁请求,线程重试次数多,做了很多无用功,而公平锁按部就班传递锁反而减少了不必要的线程调度。内置锁(synchronized)只能是非公平的,显式锁(ReentrantLock)可以自己定义,下面看代码:

    package com.wulinfeng.concurrent;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class FairAndUnfairLock {
    
        private Lock lock; // 被线程们竞争的一把锁,而且多个线程争的都是这同一把
    
        public FairAndUnfairLock(boolean isFair) {
            this.lock = new ReentrantLock(isFair);
        }
    
        /**
         * 根据是否公平设置锁,线程们进入到这个方法,说明都是来争锁的,某一线程争到了,其他线程就得等
         * 
         * @param isFair
         */
        public void fightForLock() {
            lock.lock();
            try {
                System.out.println("----" + Thread.currentThread().getName() + "获得锁.");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } finally {
                lock.unlock();
            }
        }
    
        public static void main(String[] args) {
            final FairAndUnfairLock lock = new FairAndUnfairLock(true); // 公平锁
            int threadNums = 10;
            Thread[] threads = new Thread[threadNums];
            for (int i = 0; i < threadNums; i++) {
                threads[i] = new Thread(new Runnable() {
    
                    @Override
                    public void run() {
                        System.out.println("****" + Thread.currentThread().getName() + "请求锁.");
                        lock.fightForLock();
                    }
                });
            }
    
            for (int i = 0; i < threadNums; i++) {
                threads[i].start();
            }
        }
    }

      输出结果:

    ****Thread-0请求锁.
    ----Thread-0获得锁.
    ****Thread-2请求锁.
    ****Thread-4请求锁.
    ****Thread-6请求锁.
    ****Thread-8请求锁.
    ****Thread-1请求锁.
    ****Thread-3请求锁.
    ****Thread-5请求锁.
    ****Thread-7请求锁.
    ****Thread-9请求锁.
    ----Thread-2获得锁.
    ----Thread-4获得锁.
    ----Thread-6获得锁.
    ----Thread-8获得锁.
    ----Thread-1获得锁.
    ----Thread-3获得锁.
    ----Thread-5获得锁.
    ----Thread-7获得锁.
    ----Thread-9获得锁.

      从上面可以看到,线程0请求锁后立即得到,而后面的线程2-4-6-8-1-3-5-7-9依次请求锁在排队,等0释放后他们还是按这个顺序得到了锁。再来看下非公平的,把true改成false,再把这段休眠的代码注掉

            try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

    跑一把,可以看到对同一个锁竞争后获取锁顺序是乱的:

    ****Thread-0请求锁.
    ----Thread-0获得锁.
    ****Thread-1请求锁.
    ----Thread-1获得锁.
    ****Thread-6请求锁.
    ----Thread-6获得锁.
    ****Thread-4请求锁.
    ----Thread-4获得锁.
    ****Thread-2请求锁.
    ****Thread-8请求锁.
    ****Thread-3请求锁.
    ----Thread-8获得锁.
    ----Thread-3获得锁.
    ----Thread-2获得锁.
    ****Thread-5请求锁.
    ----Thread-5获得锁.
    ****Thread-7请求锁.
    ----Thread-7获得锁.
    ****Thread-9请求锁.
    ----Thread-9获得锁.

      上面锁被线程4获取后,2-8-3依次请求,4释放锁时本该被2获取,但2在被唤醒的过程中8刚好来了并取到了锁,8用完了2还是没完全醒过来,然后3又来了取走了锁,当3释放后锁才被完全醒过来的2拿到。

  • 相关阅读:
    button标签和input button
    获取select标签的值
    window.loaction和window.location.herf
    数组重复计数,对象方法
    js对象详解
    面试经典题型整理
    一些js小知识点整理
    事件委托能够优化js性能
    网页加载的一般顺序
    http状态码
  • 原文地址:https://www.cnblogs.com/wuxun1997/p/6980015.html
Copyright © 2011-2022 走看看