Current并发包(四) - 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(); } }
保存redis相关笔记