Lock CountDownLatch CyclicBarrier Exchanger Semaphore 原子型操作
一、概述
1.Lock是JDK1.5提供的一个接口;有效的降低死锁的几率,Lock相对于synchronize而言更加灵活
synchronized 问题:容易产生死锁;锁对象不容易找寻确定;不能在方法A中加锁,方法B中解锁
同步代码块问题:容易产生死锁 synchronized(){ } 同步方法问题:锁对象不容易找寻确定 class A { // m1方法 的锁对象是 this public synchronized void m1(){} // m2方法 的锁对象是 A.class pubilc synchronized void m2(){} } A a1 = new A(); a1.m1(); //m1方法的锁对象是 a1 A a2 = new A(); a2.m1(); //m2方法的锁对象是 a2
2.用的更多的是Lock的实现类:ReentrantLock
3.公平和非公平策略:锁如果不指定,默认是非公平策略
//给上参数true,是公平策略,不给是非公平的,公平策略:多个线程操作同一个资源的时候会去排队,执行完的线程会去排在队伍的最后面
//默认为false,是因为非公平策略下更快,synchronize默认是非公平的
Lock l = new ReentrantLock(true);
4.读写锁:
a.读锁:允许多个线程读,但不允许写
b.写锁:允许一个线程写,但不允许线程读
ReadWriteLock rw1 = new ReentrantReadWriteLock(); //加读锁 rw1.readLock().lock(); //解读锁 rw1.readLock().unlock(); //加写锁 rw1.writeLock().lock(); //解写锁 rw1.writeLock().unlock();
代码:
package com.apple.lock; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** * This is Description * * @author apple * @date 2020/06/08 */ public class LockDemo { public static void main(String[] args) throws InterruptedException { Count c = new Count(); //给上参数true,是公平策略,不给是非公平的,公平策略:多个线程操作同一个资源的时候会去排队,执行完的线程会去排在队伍的最后面 //默认为false,是因为非公平策略下更快,synchronize默认是非公平的 Lock l = new ReentrantLock(true); new Thread(new AddRunnable(c, l)).start(); new Thread(new AddRunnable(c, l)).start(); new Thread(new AddRunnable(c, l)).start(); new Thread(new AddRunnable(c, l)).start(); Thread.sleep(5000); System.out.println(c.getCount()); } } class AddRunnable implements Runnable { private Count c; private Lock l; public AddRunnable(Count c, Lock l) { this.c = c; this.l = l; } @Override public void run() { //加锁 l.lock(); for (int i = 0; i < 100; i++) { c.setCount(c.getCount() + 1); } //解锁 l.unlock(); } } class Count { private int count; public int getCount() { return count; } public void setCount(int count) { this.count = count; } }
二、其他
1.CountDownLatch - 闭锁/线程递减锁,用于进行线程计数,当达到指定的计数的时候,会放开阻塞允许后续线程执行
package com.apple.lock; import java.util.concurrent.CountDownLatch; /** * This is Description * * @author apple * @date 2020/06/09 */ public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { /** * 场景:老师和4个考生进行考试,在老师和4个学生进入考场后才能开始考试 */ CountDownLatch cdl = new CountDownLatch(5); 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(); //在计数归零之前,就需要陷入阻塞 cdl.await(); System.out.println("开始考试"); } } class Teacher implements Runnable { private CountDownLatch cdl; public Teacher(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { System.out.println("监考老师到达考场~~~"); //减少1个 cdl.countDown(); } } class Student implements Runnable { private CountDownLatch cdl; public Student(CountDownLatch cdl) { this.cdl = cdl; } @Override public void run() { System.out.println("学生到达考场~~~"); //减少1个 cdl.countDown(); } }
2.CyclicBarrier:栅栏,用于进行线程的计数
场景:跑步比赛中,所有运动员都先到达起跑线(栅栏),然后再一起跑出去
package com.apple.lock; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * This is Description * * @author apple * @date 2020/06/09 */ public class CyclicBarrierDemo { public static void main(String[] args) { //参数:表示对多少线程计数 CyclicBarrier cb = new CyclicBarrier(4); 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(); } } /** * 运动员 */ class Runner implements Runnable { private CyclicBarrier cb; public Runner(CyclicBarrier cb) { this.cb = cb; } @Override public void run() { try { String name = Thread.currentThread().getName(); //每个线程准备的时间 Thread.sleep((long) (Math.random() * 1000)); System.out.println("运动员-" + name + "-到了起跑线"); //每个运动员到了起跑线后,需要等待 //每await一次,就会减少一个计数 cb.await(); //当计数为0,意味着可以放开阻塞,即 cb.await() 到 0 System.out.println("运动员-" + name + "-跑了出去"); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }
注意:CountDownLatch 和 CyclicBarrier 的区别
例如:周末,有6个人去吃饭,其中5个人到了,还有一个人没有到,这时对服务员说,晚点上菜。这种场景下,对于服务员来说就是 CountDownLatch
而对6个人来说是 CyclicBarrier 。为什么对于服务员是CountDownLatch,因为等第6个人到齐后,开始上菜是一个新的线程事件
3.Exchanger:交换机,用于交换两个线程之间的信息,只能交换2个
package com.apple.lock; import java.util.concurrent.Exchanger; /** * This is Description * 生产者和消费者之间进行商品与钱的交换 * 生产者给商品货物 * 消费者给金钱 * * @author apple * @date 2020/06/09 */ 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 = ex.exchange("货物"); System.out.println("生产者收到消费者交换的:" + info); } 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 = ex.exchange("钱"); System.out.println("消费者收到生产者交换的:" + info); } catch (InterruptedException e) { e.printStackTrace(); } } }
4.Semaphore:信号量
如果有空余的信号,则来的线程可以取得信号执行,如果没有空余的信号,来的线程就会被阻塞直到有空余信号 - 实际过程中往往用于进行”限流“操作
如:公交、地铁 - 空座椅个数 餐馆 - 空桌子数,如果有空余的座位,可以去坐,如果没有,则需要等待
package com.apple.lock; import java.util.concurrent.Semaphore; /** * This is Description * * @author apple * @date 2020/06/09 */ 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 { //信号量需要减少1个,即少一个桌子被占用 s.acquire(); System.out.println("过来一个人,一个桌子就被占用"); Thread.sleep((long) (Math.random() * 10000)); //占用时间//信号增加 s.release(); } catch (InterruptedException e) { e.printStackTrace(); } } }
三、原子型操作
1.可以认为原子性操作是针对属性的锁机制,实际开发中可以用原来习惯的锁来代替
package com.apple.lock; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; /** * This is Description * * @author apple * @date 2020/06/09 */ 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.ai.incrementAndGet(); } cdl.countDown(); } }