java基础 ---- 公平锁和非公平锁以及同步屏障

公平锁和非公平锁

一、原因
1. CPU在调度线程的时候是从等待队列里随机挑选一个线程,由于这种随机性所以是无法保证线程先到先得的(synchronized控制的锁就是这种非公平锁),但这样就会产生饥饿现象,即有些线程(优先级较低的线程)可能永远也无法获取CPU的执行权,优先级高的线程会不断的强制它的资源。那么如何解决饥饿问题呢,这就需要公平锁了

  1. 产生饥饿的另一个原因是: 某个线程占据资源不释放,那其他需要该资源的线程只能处于无限等待中。在这里主要解决第一种饥饿问题

二、什么是公平锁
公平锁可以保证线程按照时间的先后顺序执行,避免饥饿现象的产生。但公平锁的效率比较低,因为要实现顺序执行,需要维护一个有序队列,即采用公平锁的时候,几乎不会出现同一线程连续执行多次

三、代码

// 代码1: 会发现:当采用公平锁的时候,线程不会出现同一线程连续执行多次;采用非公平锁的时候,会出现同一线程连续执行多次
package com.lock;

import java.util.concurrent.locks.ReentrantLock;

public class TestLock2 {

    // 公平锁
    private static final ReentrantLock fairLock = new ReentrantLock(true);
    // 非公平锁
    private static final ReentrantLock nonFairLock = new ReentrantLock(false);

    public static void main(String[] args) throws Exception{
        for (int i = 0; i < 3; i++) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    while (true) {
                        try {
                            fairLock.lock();
                            nonFairLock.lock();
                            System.out.println(Thread.currentThread().getName() + "获得了锁  缓存队列大小为:"
                                    + fairLock.getQueueLength());
                        } finally {
                            fairLock.unlock();
                            nonFairLock.unlock();
                        }
                    }
                }
            }, "test" + i).start();
        }
    }

}

// 代码2  结论见:  http://blog.csdn.net/kjfcpua/article/details/8541433
package com.lock;

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static java.lang.System.out;
public class TestLock implements Runnable{

    public enum LockType {
        JVM, JUC
    }
    public static LockType lockType;

    public static final long ITERATIONS = 5L * 100L * 100L;
    public static long counter = 0L;

    public static final Object jvmLock = new Object();
    // 公平锁与非公平锁
    public static final Lock jucLock = new ReentrantLock(true);
    private static int numThreads;

    private final long iterationLimit;
    private final CyclicBarrier barrier;
    private long localCounter = 0L;

    public long getLocalCount() {
        return this.localCounter;
    }


    public TestLock(final CyclicBarrier barrier, final long iterationLimit) {
        this.iterationLimit = iterationLimit;
        this.barrier = barrier;
    }

    public static void main(String[] args) throws Exception {
        lockType = LockType.valueOf("JUC");
        numThreads = 8;

        final long start = System.nanoTime();
        runTest(numThreads, ITERATIONS);
        final long duration = System.nanoTime() - start;

        out.printf("%d threads, duration %d (ns)\n", numThreads, duration);
        out.printf("%d ns/op\n", duration / ITERATIONS);
        out.printf("%d ops/s\n", (ITERATIONS * 1000000000L) / duration);
        out.println("counter = " + counter);
    }

    private static void runTest(final int numThreads, final long iterationLimit) throws Exception{
        CyclicBarrier barrier = new CyclicBarrier(numThreads);
        Thread[] threads = new Thread[numThreads];
        TestLock[] testLocks = new TestLock[numThreads];
        for (int i = 0; i < threads.length; i++) {
            testLocks[i] = new TestLock(barrier, iterationLimit);
            threads[i] = new Thread(testLocks[i]);
        }

        for (Thread t : threads) 
            t.start();

        for (Thread t : threads) 
            t.join();

        for (int i = 0; i < threads.length; i++) {
            out.printf("%d thread, local counter = %d\n", i, testLocks[i].getLocalCount());
        }
    }

    @Override
    public void run() {
        try {
            System.out.println(
                    "Number waiting = " + barrier.getNumberWaiting() + "; Parties = " + barrier.getParties());
            // 同步屏障
            barrier.await();
        }  catch (Exception e) {
            e.printStackTrace();
        }

        switch (lockType) {
        case JVM: jvmLockInc(); break;
        case JUC: jucLockInc(); break;
        }
    }

    private void jucLockInc() {
        while (true) {
            long count = 0L;
            jucLock.lock();
            try {
                ++counter;
                count = counter;
            } finally {
                jucLock.unlock();
            }

            localCounter++;
            if (count >= iterationLimit) 
                break;
        }
    }


    private void jvmLockInc() {
        while (true) {
            long count = 0L;
            synchronized (jvmLock) {
                ++counter;
                count = counter;
            }

            localCounter++;
            if (count >= iterationLimit)
                break;
        }
    }
}

同步屏障
一、定义
CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
通俗: 创建同步屏障时,会传递一个线程参数,当执行CyclicBarrier.await()方法时,只有当此时的await方法的线程数与传递的线程参数一致时,才会执行接下来的语句

猜你喜欢

转载自blog.csdn.net/miracle_8/article/details/79321836