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. iOS开发ReactiveCocoa学习笔记(五)

    ReactiveCocoa常见操作方法介绍: demo地址:https://github.com/SummerHH/ReactiveCocoa.git filter ignore ignoreValu ...

  2. Spring AOP初步总结(三)

    最近遇到一个新需求:用户多次点击提交订单发生多次扣款,一开始准备配置数据库事务,但后来发现这种方法白白浪费很多资源,就改为利用接口上的切面对请求做拦截,并将当前登陆的用户存进Redis缓存,废话不说了 ...

  3. hibernate课程 初探单表映射2-3 session简介

    hibernate流程: 1 配置对象Configurateion 读取 hibernate.cfg.xml 2 会话工厂SessionFactory 读取 user.hbm.xml(创建销毁相当耗费 ...

  4. 无法定位程序输入点到_ftol2于动态链接库msvcrt.dll的错误的解决

    作者:朱金灿 来源:http://blog.csdn.net/clever101 今天同事在Windows XP系统上运行程序遇到这样一个错误: 我试了一下,在Win7上运行则没有这个错误.只是程序运 ...

  5. 2013 QConf上海软件开发大会总结

    带着工作中的一些疑问,我参加了在上海举办的QConf 全球软件开发大会.会议以主题的形式按分会场召开,我主要选择知名网站案例分析.大数据处理技术.高效能团队建设和金融系统架构与设计四个主题内容.三天会 ...

  6. JavaScript_对象

    1.  直接创建实例: //简单对象 var person1 = new Object(); person1.name = "Mike"; person1.age = 29; pe ...

  7. linux 命令——56 ss(转)

    ss是Socket Statistics的缩写.顾名思义,ss命令可以用来获取socket统计信息,它可以显示和netstat类似的内容.但ss的优势在于它能够显示更多更详细的有关TCP和连接状态的信 ...

  8. linux 命令——44 top (转)

    top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器.下面详细介绍它的使用方法.top是 一个动态显示过程,即可以通过用户按键来不断刷 ...

  9. tomcat服务器配置域名访问项目server.xml

    <?xml version='1.0' encoding='utf-8'?> <!-- Licensed to the Apache Software Foundation (ASF ...

  10. Linux 文件的压缩与解压

    1.  tar结尾压缩命令 [root@test ~]# tar -cvf grub.tar /boot/grub/ 查看压缩包文件 [root@test ~]# tar -vtf grub.tar ...