0 前言

本文通过使用synchronized以及Lock分别完成“生产消费场景”,再引出两种锁机制的关系和区别,以及一些关于锁的知识点。

本文原创,转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52401134


1. synchronized, wait, notify结合实现生产消费场景

1.1 生产者类

/*
*@author SEU_Calvin
*@date 2016/09/01
*/
public class Producer implements Runnable {
@Override
public void run() {
int count = LockTest.count;
while (count <= 3) {
synchronized (LockTest.obj) {
LockTest.count++;
System.out.println("生产者生产产品...现在有"+LockTest.count+"个");
if(LockTest.count >= 3){
System.out.println("现在产品充足,待消费...");
LockTest.obj.notify();// 主动释放对象锁
try {
LockTest.obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
}
}
}


1.2 消费者类

/*
*@author SEU_Calvin
*@date 2016/09/01
*/
public class Consumer implements Runnable {
@Override
public synchronized void run() {
int count = LockTest.count;
while (count >= 0) {
synchronized (LockTest.obj) {
LockTest.count--;
System.out.println("消费者消费产品...现在有"+LockTest.count+"个");
if(LockTest.count <= 0){
System.out.println("现在产品缺货,待生产...");
LockTest.obj.notify(); // 主动释放对象锁
try {
LockTest.obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
}
}
}

1.3 测试

public class LockTest {
public static final Object obj = new Object();
public static int count = 0;
public static void main(String[] args) {
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
} }


1.4 运行结果



这个实例比较简单,主要是通过synchronized,wait, notify结合来实现线程的顺序切换。

2. Lock类

除了wait()、notify()以及synchronized协作完成线程同步之外,使用Lock也可以达到同样的目的。

/*
*@author SEU_Calvin
*@date 2016/09/01
*/
public class ReentrantLockTest {
private volatile int stopFalg = 10;//控制程序执行次数
private volatile int count = 0;
private Lock lock = new ReentrantLock();
private ArrayList<Thread> threads = new ArrayList<Thread>(); public static void main(String[] args) throws InterruptedException {
final ReentrantLockTest test = new ReentrantLockTest();
new Thread("Producer") { //开启生产者线程
public void run() {
test.threads.add(this);
while (test.stopFalg > 0) {
test.operateResource(this.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();
Thread.sleep(1000); //保证生产者线程先启动,继而两者同时生产、消费
new Thread("Consumer") { //开启消费者线程
public void run() {
test.threads.add(this);
while (test.stopFalg > 0) {
test.operateResource(this.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();
} public void operateResource(String id) {
lock.lock(); //lock开启,锁两个线程都会访问到的同一区域的代码
try {
if ("Producer".equals(id)) { //判断线程类型
if (count < 10) {
count++;
stopFalg--;
System.out.println("Producer=>" + count);
}
} else if ("Consumer".equals(id)) {//判断线程类型
if (count > 0) {
count--;
System.out.println("Consumer=>" + count);
}
}
} finally {
lock.unlock();//必须unlock
}
}
}

2.1 运行结果


3. 两者关系与区别汇总

从上面两个实例的实现可以引出Synchronized和Lock的关系:

(1)ReentrantLock与synchronized有相同的并发性和内存语义。但是ReentrantLock还包含了区别于synchronized的以下特性。

(2)等待可中断:在持有锁的线程长时间不释放锁的时候,等待的线程可以选择放弃等待。有效防止了死锁。

lock();//用来获取锁,如果锁已被其他线程获取,则进行等待
tryLock(); //尝试获取锁,若成功返回true,失败(即锁已被其他线程获取)则返回false
tryLock(long timeout, TimeUnit unit); //在拿不到锁时会等待一定的时间
//两个线程同时通过lock.lockInterruptibly()想获取某个锁时
//若线程A获取到了锁,而线程B在等待
//线程B调用threadB.interrupt()方法能够中断线程B的等待过程
lockInterruptibly();

(3)公平锁:按照申请锁的顺序来获得锁。synchronized是非公平锁。ReentrantLock可以通过构造函数实现公平锁。

new RenentrantLock(boolean fair);

(4)绑定多个Condition,添加多个检控条件:通过多次new Condition可以获得多个Condition对象。

(5)ReentrantLock可以获取各种锁的信息,比如可以查看锁的状态,锁是否被锁上了。可以查看当前有多少线程在等待锁。

(6)Lock使用更灵活、性能更佳。性能的优化体现在,当许多线程都想访问共享资源时,JVM可以花更少的时间来调度线程,把更多时间用在执行线程上。

(7)由于synchronized是在JVM层面实现的,因此系统可以监控锁的释放与否,而ReentrantLock使用代码实现的,系统无法自动释放锁,需要在代码中finally子句中显式释放锁lock.unlock()。同时由于ReentrantLock是类,使用时需要import相关类。

(8)总结:在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态。

4. 可重入锁

synchronized以及Lock类锁,两者都是可重入锁。

class MyClass {
public synchronized void method1() {
method2();
}
public synchronized void method2() {
}
}

可重入锁的意思是,当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。假如synchronized不具备可重入性,此时线程A需要重新申请锁。但是线程A已经持有了该对象的锁,这样线程A会一直等待永远不会获取到的锁。

Java并发——synchronized和ReentrantLock的联系与区别的更多相关文章

  1. java并发编程——通过ReentrantLock,Condition实现银行存取款

         java.util.concurrent.locks包为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器.该框架允许更灵活地使用锁和条件,但以更难用的语法为代价. Lock 接口 ...

  2. java 并发——synchronized

    java 并发--synchronized 介绍 在平常我们开发的过程中可能会遇到线程安全性的问题,为了保证线程之间操作数据的正确性,我们第一想到的可能就是使用 synchronized 并且 syn ...

  3. Java并发系列[5]----ReentrantLock源码分析

    在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可 ...

  4. Java并发——synchronized关键字

    前言: 只要涉及到Java并发那么我们就会考虑线程安全,实际上能够实现线程安全的方法很多,今天先介绍一下synchronized关键字,主要从使用,原理介绍 一.synchronized的使用方法 1 ...

  5. Java并发-Synchronized关键字

    一.多线程下的i++操作的并发问题 package passtra; public class SynchronizedDemo implements Runnable{ private static ...

  6. Java锁Synchronized对象锁和类锁区别

    java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁.线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁.获得内置锁的唯一途径就是进入这个锁的保 ...

  7. Java并发编程基础-ReentrantLock的机制

    同步锁: 我们知道,锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源,在Lock接口出现之前,Java应用程序只能依靠synchronized关键字来实现同步锁 ...

  8. [Java] [Lock] [Synchronized VS ReentrantLock]

    Overview java编写多线程程序时,为了保证线程安全,需要对数据进行同步,经常用到的两种同步方式就是synchronized和重入锁ReentrantLock. 相似点 都是加锁方式 都是阻塞 ...

  9. java并发编程基础-ReentrantLock及LinkedBlockingQueue源码分析

    ReentrantLock是一个较为常用的锁对象.在上次分析的uil开源项目中也多次被用到,下面谈谈其概念和基本使用. 概念 一个可重入的互斥锁定 Lock,它具有与使用 synchronized 相 ...

随机推荐

  1. 判断dataset表中是否存在 某列

    DataSet ds ; ds.Tables[0].Columns.Contains("a") 同样适用于 datarow dr ; dr.Table.Columns.Contai ...

  2. Java基础反射-调用类

    Student类 package com.test.wang; import java.lang.reflect.Constructor; import java.lang.reflect.Field ...

  3. Django组件:用户认证组件

    一丶用户认证 1.auth模块 from django.contrib import auth django.contrib.auth中提供了许多方法,这里主要介绍其中的三个: (1).authent ...

  4. 详细讲解:yii 添加外置参数 高级版本

    在YII中,添加状态参数的形式 首先,我们在advanced\common\config\params.php文件中,添加我们要设置的参数: 要在控制器中进行使用的话,形式为:\Yii::$app-& ...

  5. 2018.10.29 NOIP2018模拟赛 解题报告

    得分: \(70+60+0=130\)(\(T3\)来不及打了,结果爆\(0\)) \(T1\):简单的求和(点此看题面) 原题: [HDU4473]Exam 这道题其实就是上面那题的弱化版,只不过把 ...

  6. MySql-8.0.12 安装教程随笔

    下载地址: https://www.mysql.com/downloads/ 现在最下边的社区版本,也就是免费版本 之后我们会看到有两个选择的下载,一个为zip压缩包格式,一个是Install版本,个 ...

  7. 问题002:我们要使用的Java是哪个版本的?什么是JVM、JRE、JDK、IDE、API?

    三个版本:1.java SE 标准版 2.java EE企业版 3.Java ME 小型版本 JVM (java virtual machine) java虚拟机 JRE(java runtime e ...

  8. ARC机制中的Strong和weak

    什么是ARC Automatic Reference Counting,自动引用计数,即ARC,可以说是WWDC2011和iOS5所引入的最大的变革和最激动人心的变化.ARC是新的LLVM 3.0编译 ...

  9. NSCopying

    ///< .h @interface ChatManager : NSObject <NSCopying> @property (nonatomic) NSUInteger inde ...

  10. IDEA Tomcat 日志和输出中文乱码问题

    说明:该方法是在网上查找的其他方法均无效的情况下自己摸索出的设置.既然别人有效的设置在我这里无效,那么以下设置自然有可能无效.建议综合多个搜索结果进行尝试. 仅需要进行两处设置 1. 更改 IDEA ...