多线程学习第七课

断水流大师兄

1、AQS

1、概念:AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒 时锁分配的机制,这个机制 AQS 是用 CLH 队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
2、简单的AQS图解:

3、实现案例

4、AQS 使用一个 int 成员变量state来表示同步状态,这个成员变量是volatitlex修饰的,通过内置的 FIFO 队列来完成获取资源线程的排队工作。AQS 使用 CAS 对该同步状态进行原子操作实现对其值的修改。
// 定义共享资源状态
private volatile int state;

// 获取共享资源的状态
protected final int getState() {
        return state;
    }
// 设置共享资源的状态
protected final void setState(int newState) {
        state = newState;
    }
// CAS竞争state
protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }
以ReentrantLock为例,state初始化为0,表示未锁定状态。
A线程lock时,会调用tryAcquire独占该锁并将state+1。此后,其他线程再tryAcquire时就会失败,直到A线程unlock到state=0(即释放锁)为止,其它线程才有机会获取该锁。
当然,释放锁之前,A线程自己是可以重复获取此锁的(state会累加),这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。

5、关于线程的阻塞策略

2、ReentrantLock

ReentrantLock 支持公平锁和非公平锁,可重入锁ReentrantLock的底层是通过 AQS实现。
ReentrantLock 类内部总共存在Sync、NonfairSync、FairSync三个类,NonfairSync与 FairSync类继承自 Sync类,Sync类继承自 AbstractQueuedSynchronizer抽象类。下面逐个进行分析

ReentrantLock与Synchronized的区别:

ReentrantLock的使用:
// ReentrantLock的构造函数,默认使用非公平锁
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

// ReentrantLock使用demo
public void test () throw Exception {
     // 1.初始化选择公平锁、非公平锁
     ReentrantLock lock = new ReentrantLock(true);
     // 2.可用于代码块
     lock.lock();
     try {
         try {
             // 3.支持多种加锁方式,比较灵活
             // 4.具有可重入特性
             if(lock.tryLock(100, TimeUnit.MILLISECONDS)){ }
         } finally {
             // 5.手动释放锁
             lock.unlock()
         }
     } finally {
         lock.unlock();
     }
 }
ReentrantLock实现可重入:
// 1、非公平锁
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            // 获取锁成功,设置同步器exclusiveOwnerThread为当前线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 同步器exclusiveOwnerThread为当前线程,仅累加同步状态
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}
// 2、公平锁
protected final boolean tryAcquire(int acquires) {
    Thread current = Thread.currentThread();
    int c = getState();
    // c & 0x0000FFFF,查询写状态值
    int w = exclusiveCount(c);
    if (c != 0) {
        // 状态值 != 0 && 写状态值 == 0,表示有当前有读锁
        // 独占线程是当前线程,可重入
        if (w == 0 || current != getExclusiveOwnerThread())
            return false;
        if (w + exclusiveCount(acquires) > MAX_COUNT)
            throw new Error("Maximum lock count exceeded");
        // Reentrant acquire
        setState(c + acquires);
        return true;
    }
    if (writerShouldBlock() ||
        !compareAndSetState(c, c + acquires))
        return false;
    setExclusiveOwnerThread(current);
    return true;
}

3、练习

3.1 给三个文件,每个文件里面有10万条记录每一行是一个0-100000之间的随机数  我们通过启动三个线程 来统计下这个文件里面大于 95000 数字有多少? 
public class MyTest {
    public static void main(String[] args) throws InterruptedException {

        List<Long> array1 = getRandomArray();
        List<Long> array2 = getRandomArray();
        List<Long> array3 = getRandomArray();

        MyCounter myCounter = new MyCounter();
        Thread thread1 = new Thread(new Runner(myCounter, array1));
        Thread thread2 = new Thread(new Runner(myCounter, array2));
        Thread thread3 = new Thread(new Runner(myCounter, array3));

        thread1.start();
        thread2.start();
        thread3.start();

        thread1.join();
        thread2.join();
        thread3.join();

        System.out.println("统计大于95000的数字共有:" + myCounter.getSum());
    }

    static class MyCounter {
        ReentrantLock reentrantLock = new ReentrantLock(true);
        protected volatile Integer sum = 0;
        public void add(int value) {
            reentrantLock.lock();
            try {
                this.sum = this.sum + value;
            } catch (Throwable throwable) {
                reentrantLock.unlock();
            } finally {
                reentrantLock.unlock();
            }
        }

        public Integer getSum() {
            return this.sum;
        }
    }


    static class Runner implements Runnable{
        private MyCounter counter;
        private List<Long> data;
        Runner(MyCounter counter, List<Long> data) {
            this.counter = counter;
            this.data = data;
        }
        @Override
        public void run() {
            for (int i = 0; i < data.size(); i++) {
                if (data.get(i) > 95000) {
                    counter.add(1);
                }
            }
        }
    }

    private static List<Long> getRandomArray() {
        List<Long> result = Lists.newArrayList();
        Random random = new Random();
        for (int j = 1; j <= 100000; j++) {
            int i = random.nextInt(100000);
            result.add(Long.valueOf(i));
        }
        return result;
    }
}
阅读 325
6 声望
0 粉丝
0 条评论
6 声望
0 粉丝
文章目录
宣传栏