Java并发包中常用的类及其用法

介绍几种Java并发包(juc)中常用的类及其用法。

1.ReentrantLock

ReentrantLock可重入锁,与synchronized的区别:
1.ReentrantLock需要自己手动加锁解锁,synchronized的不需要
2.ReentrantLock可以tryLock尝试获取锁(指定时间内),锁未被占用则获取锁(锁会在tryLock返回true后被调用线程获取),则返回true,否则返回false
3.ReentrantLock可以设定公平锁和非公平锁,synchronized只有非公平锁
代码演示:

public class ReentrantLockDemo {
    private static ReentrantLock lock = new ReentrantLock();

    private void testLock() {
        try {
            lock.lock();
            // todo 原子操作
        } finally {
            lock.unlock();
        }
    }

    private void testTryLock() {
        boolean isLocked = false;
        try {
            // 尝试获取锁,规定时间内获取到锁则返回true,超时未获取锁则返回false
            isLocked = lock.tryLock(5, TimeUnit.SECONDS);
            if (isLocked) {
                for (int i = 0; i < 10; i++) {
                    TimeUnit.SECONDS.sleep(1);
                    System.out.println(Thread.currentThread().getName() + ": " + i);
                }
            } else {
                System.out.println(Thread.currentThread().getName() + "获取锁超时");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (isLocked) {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        ReentrantLockDemo reentrantLockDemo = new ReentrantLockDemo();

        // 线程1
        new Thread(reentrantLockDemo::testTryLock).start();
        // 线程2
        new Thread(reentrantLockDemo::testTryLock).start();
    }
}

2.CountDownLatch

CountDownLatch:门闩。在一个线程中设定一个预定值(大于0),然后调用await方法等待。其他线程每调用一次countDown方法,预定值就减一,当预定值为0时,await线程就会被唤醒。就像一扇门,被n个门闩拴住,当门闩被一个个的抽掉,门就打开了
代码演示:

public class CountDownLatchDemo {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(3);

        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + "开始");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "结束");
                latch.countDown();
            }).start();
        }

        try {
            latch.await(); // 主线程等待
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("结束");
    }
}

3.CyclicBarrier

CyclicBarrier:栅栏,分界线。例如大家一起坐大巴车,每个人上车以后等待(await,线程等待),等到所有人(CyclicBarrier的初始参数parties)都上车后,车就出发(所有线程唤醒)
代码演示:

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        // param1: 等待线程数;param2:所有线程都到齐后触发行为
        CyclicBarrier barrier = new CyclicBarrier(3, ()-> System.out.println("人满,出发"));

        for (int i = 0; i < 3; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName() + "上车");
                try {
                    barrier.await();
//                    System.out.println(Thread.currentThread().getName() + "dosomething");
                } catch (InterruptedException | BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }).start();
            // 每1s启动一个线程
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

4.Semaphore

Semaphore:信号量。这个锁用于控制对某组资源的访问权限,即同时只有指定数量的线程可以获得执行权。例如银行大厅有3个人工窗口,同时只有三个人能在窗口办公,其他人都需要等待
使用场景:限流
代码演示:

public class SemaphoreDemo {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire();// 获取锁
                    System.out.println(Thread.currentThread().getName() + "正在执行...");
                    TimeUnit.SECONDS.sleep(3);
                    semaphore.release();// 释放锁
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

5.ReadWriteLock

ReadWriteLock: 读写锁。写锁是互斥锁,写的时候其他线程不能写,也不能读;读锁是共享锁,读的时候其他线程也可以读,但是不能写。由于读锁是共享锁,多个线程可以同时读数据,所以效率会比直接加一个synchronized效率高
代码演示:

public class ReadWriteLockDemo {
    static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    static Lock readLock = readWriteLock.readLock();// 读锁
    static Lock writeLock = readWriteLock.writeLock(); // 写锁
    static int value = 0;

    public static void read(Lock lock) {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "开始读...");
            TimeUnit.SECONDS.sleep(1);
            System.out.println(Thread.currentThread().getName() + "读结束,value = " + value);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void write(Lock lock, int v) {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "开始写...");
            TimeUnit.SECONDS.sleep(1);
            value = value + v;
            System.out.println(Thread.currentThread().getName() + "写完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        // 模拟10个线程读
        for (int i = 0; i < 10; i++) {
            new Thread(() -> ReadWriteLockDemo.read(readLock)).start();
        }
        // 模拟3个线程写
        for (int i = 0; i < 3; i++) {
            new Thread(() -> ReadWriteLockDemo.write(writeLock, 1)).start();
        }

    }
}

6.LockSupport

LockSupport用于控制一个线程的停止与唤醒,调用park方法则线程阻塞,调用unpark方法则线程重新运行。与wait/notify的区别:
1.wait/notify必须在同步代码中使用,多个线程竞争一把锁的情况下,线程调用wait让出锁,notify唤醒其他线程来竞争锁
2.unpark可以先于park调用,成对出现则线程不会停止

public class LockSupportDemo {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
                if (i == 5) {
                    LockSupport.park(); // 线程停止
                }
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
//        LockSupport.unpark(t);
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        LockSupport.unpark(t); // 解除线程停止状态
        System.out.println("线程继续运行...");
    }
}

7.Condition

Condition:用于线程间通信,其重要方法await/signal/signalAll,作用类似于Object的wait/notify/notifyAll
区别:
1.Condition的使用需要依赖ReentrantLock
2.每个Condition对象都相当于一个队列,线程调用了Condition对象的await方法,就会到相应的队列中等待
例如有两个Condition对象:producer,consumer,线程调用producer.await(),则线程会在producer队列中等待,线程调用consumer.await(),则线程会在consumer队列中等待.此时调用producer.signalAll(),会唤醒producer队列中等待的所有线程去竞争锁,consumer同理
代码演示:

/**
 * demo:
 * 定义一个固定容量的容器,容量为10,能够支持2个生产者put和10个消费者get。
 * 容器元素为最大容量时,生产者停止put,需通知消费者get;容器元素为0时,消费者停止get,需通知生产者put
 * created by it_hushuai
 * 2020/12/5 15:43
 */
public class ConditionDemo {
    public static void main(String[] args) {
        ContainerDemo<String> container = new ContainerDemo<>();

        // 启动消费者线程
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                for (int j = 0; j < 5; j++) {
                    System.out.println(Thread.currentThread().getName() + "正在消费元素:" + container.get());
                }
            }).start();
        }

        try {
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 启动生产者线程
        for (int i = 0; i < 2; i++) {
            new Thread(()->{
                for (int j = 0; j < 25; j++) {
                    String item = Thread.currentThread().getName() + "-" + j;
                    System.out.println(Thread.currentThread().getName() + "正在生产:" + item);
                    container.put(item);
                }
            }).start();
        }
    }

}

class ContainerDemo<T> {
    final private LinkedList<T> list = new LinkedList<>();
    final private int MAX = 10; // 容器容量
    private int count = 0;

    private ReentrantLock lock = new ReentrantLock();
    private Condition producer = lock.newCondition();
    private Condition consumer = lock.newCondition();

    public /*synchronized*/ void put(T t) {
        try {
            lock.lock();
            // 这里使用while而不使用if:当线程被唤醒后,仍需判断当前元素个数是否达到最大值
            while (list.size() == MAX) { // 只要当前list的大小为容量值10,则不能添加元素,添加元素线程阻塞
                try {
                    producer.await(); // 生产者线程等待
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // 未到达容器容量
            list.add(t);
            ++count;
            // 通知消费线程
            consumer.signalAll();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 每次只能由一个线程取出一个元素
     *
     * @return
     */
    public /*synchronized*/ Object get() {
        T t = null;
        try {
            lock.lock();
            while (list.size() == 0) { // 当容器元素为0时,停止消费
                try {
                    consumer.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            t = list.removeFirst();
            count--;
            producer.signalAll(); // 通知生产线程
        } finally {
            lock.unlock();
        }
        return t;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值