Current并发包(四) - lock

LOCK


lock概述

非静态同步方法锁是this

静态同步方法锁的锁对象是当前类的字节码

在JDK1.5中,提供了Lock接口

通过lock方法加锁, 通过该unlock方法解锁

锁的公平和非公平策略

在非公平策略下,线程的执行次数并不均等,甚至会出现较大的差距

在公平策略下,线程的执行次数应该是均等的

从效率角度考虑,非公平的效率会相对较高

synchronized使用的是非公平策略

Lock默认是非公平策略

LockDemo.java
package com.wiscom.lock;
​
public class LockDemo {
​
    static int i = 0;
    public static void main(String[] args) throws InterruptedException {
        // true 为公平策略 false为非公平策略
        Lock l = new ReentrantLock(true);
        
        new Thread(new Add(l)).start();
        new Thread(new Add(l)).start();
​
        Thread.sleep(2000);
        System.out.println(i);
    }
​
}
​
class Add implements Runnable {
    private Lock l;
​
    public Add(Lock l) {
        this.l = l;
    }
​
    @Override
    public void run() {
        // 加锁
        l.lock();
        for (int i = 0; i < 10000; i++) {
            LockDemo.i++;
        }
        // 解锁
        l.unlock();
    }
​
}
​

 

ReadWriteLock -- 读写锁

ReadWriteLock lock = new ReentrantReadWriteLock();

加读锁

lock.readLock();

解读锁

lock.readLock().unlock;

 

其他锁

CountDownLatch

闭锁/线程递减锁

对线程进行计数,在计数归零之前,线程会被阻塞

CountDownLatchDemo.java
package com.wiscom.lock;
​
public class CountDownLatchDemo {
​
    public static void main(String[] args) throws InterruptedException {
​
        CountDownLatch cdl = new CountDownLatch(6);
        new Thread(new Teacher(cdl)).start();
        new Thread(new Student(cdl)).start();
        new Thread(new Student(cdl)).start();
        new Thread(new Student(cdl)).start();
        new Thread(new Student(cdl)).start();
        new Thread(new Student(cdl)).start();
        // 在计数归零之前,需要让主函数所在线程先陷入阻塞
        // 当计数归零之后,自动的放开阻塞
        cdl.await();
        System.out.println("开始考试~~~");
    }
}
class Teacher implements Runnable {
​
    private CountDownLatch cdl;
​
    public Teacher(CountDownLatch cdl) {
        this.cdl = cdl;
    }
​
    @Override
    public void run() {
        try {
            Thread.sleep((long) (Math.random() * 10000));
            System.out.println("考官到达考场~~~");
            // 减少1个计数
            cdl.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
​
}
​
class Student implements Runnable {
​
    private CountDownLatch cdl;
​
    public Student(CountDownLatch cdl) {
        this.cdl = cdl;
    }
​
    @Override
    public void run() {
        try {
            Thread.sleep((long) (Math.random() * 10000));
            System.out.println("考生到达考场~~~");
            cdl.countDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
​
}

 

CyclicBarrier -栅栏

对线程进行计数。同样,在计数归零之前也会使线程陷入阻塞

CyclicBarrierDemo.java
package com.wiscom.lock;
​
public class CyclicBarrierDemo {
​
    public static void main(String[] args) {
​
        CyclicBarrier cb = new CyclicBarrier(6);
        
        new Thread(new Runner(cb), "1号").start();
        new Thread(new Runner(cb), "2号").start();
        new Thread(new Runner(cb), "3号").start();
        new Thread(new Runner(cb), "4号").start();
        new Thread(new Runner(cb), "5号").start();
        new Thread(new Runner(cb), "6号").start();
    }
​
}
​
class Runner implements Runnable {
​
    private CyclicBarrier cb;
​
    public Runner(CyclicBarrier cb) {
        this.cb = cb;
    }
​
    public void run() {
​
        String name = Thread.currentThread().getName();
        System.out.println(name + "到起跑线~~~");
        // 需要让线程先陷入阻塞,等到所有的线程都到了之后才能继续往下执行
        // 减少一个计数
        try {
            // 会让线程阻塞同时会自动的减少一个计数
            cb.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(name + "跑了出去~~~");
​
    }
​
}

 

CountDownLatch与CyclicBarrier对比

CountDownLatch

适合于一组线程结束之后需要开启另一组线程

CyclicBarrier

适合于一组线程到达同一个点之后再继续执行

例子

6个人到齐了,再上菜

对这个流程而言,是六个线程结束之后,再开启上菜这个新的线程,此时适用于CountDownLatchh

6个人到齐之后开始吃饭

相当于6个线程都到达同一个点后,再分别吃饭,那么此时适用于CycliBarrier

 

 

Exchanger -交换机

用于交换两个线程之间的信息

ExchangerDemo.java
package com.wiscom.lock;
​
public class ExchangerDemo {
    
    public static void main(String[] args) {
        
        Exchanger<String> ex = new Exchanger<>();
        new Thread(new Producer(ex)).start();
        new Thread(new Consumer(ex)).start();
        
    }
​
}
​
class Producer implements Runnable {
​
    private Exchanger<String> ex;
​
    public Producer(Exchanger<String> ex) {
        this.ex = ex;
    }
​
    @Override
    public void run() {
        try {
            String info = "商品";
            // 商家需要将信息交换给客户
            // 同时这个过程中商家应该受到客户交换过来的信息
            String msg = ex.exchange(info);
            System.out.println("商家收到客户给的:" + msg);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
​
    }
​
}
​
class Consumer implements Runnable {
​
    private Exchanger<String> ex;
​
    public Consumer(Exchanger<String> ex) {
        this.ex = ex;
    }
​
    @Override
    public void run() {
        try {
            String info = "钱";
            // 客户需要将信息交换给商家
            // 同时这个过程中应该收到商家交换过来的信息
            String msg = ex.exchange(info);
            System.out.println("客户收到商家给的:" + msg);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
​
}

 

Semaphore -信号量

举例

吃饭

桌子的数量是有限的

如果所有的桌子被占满,则后来的客户需要等待

等到有桌子被空出,等待的客人就可以用餐

桌子就是所谓的信号

每有一张桌子被占用,那么就相当于一个信号被获取

当所有的信号都被获取之后,后来的线程就需要阻塞

SemaphoreDemo.java
package com.wiscom.lock;
​
public class SemaphoreDemo {
​
    public static void main(String[] args) {
​
        Semaphore s = new Semaphore(5);
        for (int i = 0; i < 8; i++)
            new Thread(new Table(s)).start();
​
    }
​
}
​
class Table implements Runnable {
​
    private Semaphore s;
​
    public Table(Semaphore s) {
        this.s = s;
    }
​
    @Override
    public void run() {
        try {
            // 过来一个线程,桌子少了一张
            // 并且当没有桌子可以使用的时候
            // 后来的线程就需要陷入阻塞
            s.acquire();
            System.out.println("过来一个人,占用一张桌子~~~");
            Thread.sleep((long) (Math.random() * 10000));
            System.out.println("一个人吃完饭,起身离开,空出一张桌子~~~");
            // 将这个信号给释放 --- 可用信号多了一个
            // 当信号被释放之后,被阻塞的线程就可以抢占这个信号然后执行
            s.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
​
    }
​
}

 

原子性操作(使用不多)

不会被线程调度机制打断,这种操作,一旦开始,就一致运行到结束

AtomicDemo.java
package com.wiscom.atomic;
​
public class AtomicDemo {
​
    // static int i = 0;
    static AtomicInteger ai = new AtomicInteger(0);
​
    public static void main(String[] args) throws InterruptedException {
​
        CountDownLatch cdl = new CountDownLatch(2);
        new Thread(new Add(cdl)).start();
        new Thread(new Add(cdl)).start();
        cdl.await();
        System.out.println(ai);
    }
​
}
​
class Add implements Runnable {
​
    private CountDownLatch cdl;
​
    public Add(CountDownLatch cdl) {
        this.cdl = cdl;
    }
​
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            // AtomicDemo.i++;
            AtomicDemo.ai.incrementAndGet();
        }
        cdl.countDown();
    }
​
}
​
posted @ 2020-08-25 15:10  minnersun  阅读(265)  评论(0编辑  收藏  举报