上一篇文章中,咱们已经介绍过了各类锁,让各位对锁有了必定的了解。接下来将为各位介绍锁在Java中的实现。关注个人公众号「Java面典」了解更多 Java 相关知识点。html
在 Java 中主要经过使用synchronized 、 volatile关键字,及 Lock 接口的子类 ReentrantLock 和 ReadWriteLock 等来实现加锁。java
synchronized 属于独占式的悲观锁,同时属于可重入锁。数据库
synchronized 能够把任意一个非 NULL 的对象看成锁。其在不一样场景下的做用范围以下:安全
它有多个队列,当多个线程一块儿访问某个对象监视器的时候,对象监视器会将这些线程存储在不一样的容器中。多线程
参考资料并发
比 sychronized 更轻量级的同步锁ide
使用 volatile 必须同时知足下面两个条件才能保证在并发环境的线程安全:函数
对 volatile 变量的单次读/写操做能够保证原子性的,如 long 和 double 类型变量,可是并不能保证 i++ 这种操做的原子性,由于本质上 i++ 是读、写两次操做。
ui
Java 中的锁都实现于 Lock 接口,主要方法有:this
该方法和lock()的区别在于,若是锁不可用,tryLock()不会致使当前线程被禁用。
Condition 的做用是对锁进行更精确的控制。对于同一个锁,咱们能够建立多个 Condition,在不一样的状况下使用不一样的 Condition。
可重入锁。
除了能完成 synchronized 所能完成的全部工做外,还提供了诸如可响应中断锁、可轮询锁请求、定时锁等避免多线程死锁的方法。
public class MyLock { private Lock lock = new ReentrantLock(); // Lock lock = new ReentrantLock(true); //公平锁 // Lock lock = new ReentrantLock(false); //非公平锁 private Condition condition = lock.newCondition(); //建立 Condition public void testMethod() { try { lock.lock(); //lock 加锁 // 1:wait 方法等待: //System.out.println("开始 wait"); condition.await(); // 经过建立 Condition 对象来使线程 wait,必须先执行 lock.lock 方法得到锁 // 2:signal 方法唤醒 condition.signal(); //condition 对象的 signal 方法能够唤醒 wait 线程 for (int i = 0; i < 5; i++) { System.out.println("ThreadName=" + Thread.currentThread().getName() + (" " + (i + 1))); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
共享锁(读-写锁)
CountDownLatch 是一个同步辅助类,在完成一组正在其余线程中执行的操做以前,它容许一个或多个线程一直等待。
final CountDownLatch latch = new CountDownLatch(2); new Thread() { public void run() { System.out.println("子线程" + Thread.currentThread().getName() + "正在执行"); Thread.sleep(3000); System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕"); latch.countDown(); } ; }.start(); new Thread() { public void run() { System.out.println("子线程" + Thread.currentThread().getName() + "正在执行"); Thread.sleep(3000); System.out.println("子线程" + Thread.currentThread().getName() + "执行完毕"); latch.countDown(); } ; }.start(); System.out.println("等待 2 个子线程执行完毕..."); latch.await(); System.out.println("2 个子线程已经执行完毕"); System.out.println("继续执行主线程");
CyclicBarrier 是一个同步辅助类,容许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。由于该 barrier 在释放等待线程后能够重用,因此称它为循环 的 barrier。
CyclicBarrier 中最重要的方法就是 await 方法,它有 2 个重载版本:
public static void main(String[] args) { int N = 4; CyclicBarrier barrier = new CyclicBarrier(N); for (int i = 0; i < N; i++) new Writer(barrier).start(); } static class Writer extends Thread { private CyclicBarrier cyclicBarrier; public Writer(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { try { Thread.sleep(5000); //以睡眠来模拟线程须要预约写入数据操做 System.out.println("线程" + Thread.currentThread().getName() + "写入数据完 毕,等待其余线程写入完毕"); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println("全部线程写入完毕,继续处理其余任务,好比数据操做"); } }
Semaphore 是一种基于计数的信号量。它能够设定一个阈值,基于此,多个线程竞争获取许可信号,作完本身的申请后归还,超过阈值后,线程申请许可信号将会被阻塞。Semaphore 能够用来构建一些对象池,资源池之类的,好比数据库链接池。
咱们也能够建立计数为 1 的 Semaphore,将其做为一种相似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。
它的用法以下:
// 建立一个计数阈值为 5 的信号量对象 // 只能 5 个线程同时访问 Semaphore semp = new Semaphore(5); try { // 申请许可 semp.acquire(); try { // 业务逻辑 } catch (Exception e) { } finally { // 释放许可 semp.release(); } } catch (InterruptedException e) { }
Semaphore 基本能完成 ReentrantLock 的全部工做,使用方法也有许多相似之处:
Java多线程并发03——什么是线程上下文,线程是如何调度的