synchronized和lock比对
前言:在上面的博客说了synchronized的一些用法,下面我们再来看看lock,这个出现频率也是非常高的一个。
1:获取Lock锁的几种方式
前面说了synchronized有锁对象和锁类对象,当某个线程获取锁其他线程必须等待执行完毕才可继续进行,比如线程A先获取锁,但是出现异常导致的后果就是线程B无法获取锁,会出现死锁的情况(http://www.cnblogs.com/LipeiNet/p/6475851.html),那么我们一起看看Lock是如何解决的。lock有4种方式来获取锁
1:lock.lock() 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁。此种模式和synchronized一样但是不会出现死锁
public class Lock1 {
static int value = 0;
static Lock lock = new ReentrantLock();
static class Task1 implements Runnable {
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "开始执行");
lock.lock();
try {
for (int i = 0; i < 1000000; i++) {
value++;
}
System.out.println(value);
} finally {
lock.unlock();
}
}
}
static class Task2 implements Runnable {
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "开始执行");
lock.lock();
try {
for (int i = 0; i < 1000000; i++) {
value++;
}
System.out.println(value);
} finally {
lock.unlock();
}
}
}
public static void main(String[] args) {
ExecutorService service= Executors.newCachedThreadPool();
service.execute(new Task1());
service.execute(new Task2());
service.shutdown();
}
}
输出结果很明显其中一个value是1000000,一个是2000000,效果和synchronized是一样的。但是如果我们去掉lock以后的结果呢,很明显会错,如下图这样

为啥会出现这样情况呢,是由于cpu速度极快,每次处理完毕之后并没有立即把数值放入Java内存中,而是放在写缓存区,然后由写缓存区同步到Java内存中,这样一样,如果线程1计算结果是2,但是还是到内存中,导致线程2以为value值还是1所以会重复计算,还有从结果我们也可以看出value值并不是100000说明2个线程是同步执行的。
2:lock.tryLock();
这个方法和synchronized有所不同,synchronized和lock都会等待直到获取锁。如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;当然我们可以利用while循环一直等待,直到获取锁然后进行。代码如下
public class Lock2 {
public static void main(String[] args) {
final Lock lock = new ReentrantLock();
Thread t1 = new Thread(new Runnable() {
public void run() {
String tName = Thread.currentThread().getName();
while (!lock.tryLock()) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("等待获取锁");
}
try {
for (int i = 0; i < 5; i++) {
System.out.println(tName + ":" + i);
}
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
String tName = Thread.currentThread().getName();
while (!lock.tryLock()) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("等待获取锁");
}
try {
for (int i = 0; i < 5; i++) {
System.out.println(tName + ":" + i);
}
} catch (Exception e) {
System.out.println(tName + "出错了!!!");
} finally {
System.out.println(tName + "释放锁!!");
lock.unlock();
}
}
});
t1.start();
t2.start();
}
}
3:lock.trylock(long time, TimeUnit unit)如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;unit是time的时间单位比如TimeUnit.SECONDS就是表示秒
4:lock.lockInterruptibly()如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断。也就是说如何线程没有被中断和lock.lock()的作用一样。但是如何线程被中断了,那么此时这个线程不会有任何的响应,想象这么一个场景,线程A和线程B同时执行任务,但是必须等待线程B先执行,但是执行过程中突然线程A突然被中断,那么这个时候就可能出现死锁,哪怕是在finally中加入unlock,这个时候我们就要采用lockInterruptibly()了。代码如下
public class Lock3 {
public static void main(String[] args) {
final Lock lock = new ReentrantLock();
final Thread thread1 = new Thread(new Runnable() {
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
System.out.println("等待被中断");
lock.lockInterruptibly();
} catch (InterruptedException e) {
System.out.println("我被中断了");
} finally {
lock.unlock();
}
}
});
Thread thread2 = new Thread(new Runnable() {
public void run() {
lock.lock();
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
}
thread1.interrupt();
System.out.println("线程1已经被中断");
}
});
thread1.start();
thread2.start();
}
}
2:读锁和写锁
在开发中我们最好的愿望就是写的时候加锁,但是读的时候不加锁这样会大大的提升效率,但是采用synchronized却无法满足我们的要求,如果在读的方法面前加锁那么所有的读都需要等待,如果不加锁的话那么如果现在A,B2个线程读取,C线程写入可能导致的后果就是A,B2个线程取得数据不一致,明明同一种业务场景但是获取值却不同。好了lock的读锁和写锁帮助我们实现这种功能。
public class ReadWriteLockTest {
private int value = 0;
ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void add(int value) {
Lock writeLock = readWriteLock.writeLock();
writeLock.lock();
try {
TimeUnit.SECONDS.sleep(3);
System.out.println("添加开始时间:" + new Date());
this.value += value;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
writeLock.unlock();
}
}
public void getValue() {
Lock readLock = readWriteLock.readLock();
readLock.lock();
try {
TimeUnit.SECONDS.sleep(1);
System.out.println("获取开始时间:" + new Date());
System.out.println(value);
} catch (InterruptedException e) {
} finally {
readLock.unlock();
}
}
public static void main(String[] args) {
final ReadWriteLockTest readWriteLockTest = new ReadWriteLockTest();
Runnable task1 = new Runnable() {
public void run() {
readWriteLockTest.add(100);
}
};
Runnable task2 = new Runnable() {
public void run() {
readWriteLockTest.getValue();
}
};
ExecutorService service = Executors.newCachedThreadPool();
for (int i=0;i<2;i++){
service.execute(task1);
}
for (int i=0;i<2;i++){
service.execute(task2);
}
for (int i=0;i<2;i++){
service.execute(task1);
}
service.shutdown();
}
}
运行结果:
从这个结果我们很明显的可以总结读锁和写锁
第一:如果执行写的时候,读和写必须等待
第二:如果执行读的时候,写必须等待,而读却不用等待
也就是说读和写必须存在先后顺序,不管是先读还是先写。
3:总结
相同点:lock能实现synchronized所有可以实现的
不同点:
1:lock不容易出现死锁,而synchronized如果某个线程出现异常就会产生死锁
2:lock更加灵活,可以通过tryLock来验证是否获取锁,在线程中断也同样可以处理
3:lock有读写锁在并发量大的时候具有很大的优势,因为读的情况一般会比写多很多
synchronized和lock比对的更多相关文章
- 线程安全、数据同步之 synchronized 与 Lock
本文Demo下载传送门 写在前面 本篇文章讲的东西都是Android开源网络框架NoHttp的核心点,当然线程.多线程.数据安全这是Java中就有的,为了运行快我们用一个Java项目来讲解. 为什么要 ...
- 【转载】synchronized 与 Lock 的那点事
最近在做一个监控系统,该系统主要包括对数据实时分析和存储两个部分,由于并发量比较高,所以不可避免的使用到了一些并发的知识.为了实现这些要求,后台使用一个队列作为缓存,对于请求只管往缓存里写数据.同时启 ...
- 转:synchronized和LOCK的实现原理---深入JVM锁机制
JVM底层又是如何实现synchronized的? 目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug ...
- synchronized 与 Lock 的那点事
最近在做一个监控系统,该系统主要包括对数据实时分析和存储两个部分,由于并发量比较高,所以不可避免的使用到了一些并发的知识.为了实现这些要求,后台使用一个队列作为缓存,对于请求只管往缓存里写数据.同时启 ...
- JAVA中synchronized和lock详解
目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea.本文并不比较synchronize ...
- Java多线程编程(四)—浅谈synchronized与lock
一.共享资源竞争问题 在Java语言的并发编程中,由于我们不知道线程实际上在何时运行,所以在实际多线程编程中,如果两个线程访问相同的资源,那么由于线程运行的不确定性便会在这种多线程中产生访问错误.所以 ...
- Java中synchronized和Lock的区别
synchronized和Lock的区别synchronize锁对象可以是任意对象,由于监视器方法必须要拥有锁对象那么任意对象都可以调用的方法所以将其抽取到Object类中去定义监视器方法这样锁对象和 ...
- 【Java】synchronized与lock的区别
从Java 5之后,在java.util.concurrent.locks包下提供了另外一种方式来实现同步访问,那就是Lock. 也许有朋友会问,既然都可以通过synchronized来实现同步访问了 ...
- 详解synchronized与Lock的区别与使用
知识点 1.线程与进程 在开始之前先把进程与线程进行区分一下,一个程序最少需要一个进程,而一个进程最少需要一个线程.关系是线程–>进程–>程序的大致组成结构.所以线程是程序执行流的最小单位 ...
随机推荐
- windows svn利用钩子实现代码同步到web目录
思路: 找 到SVN Server中的仓库(Repositories)文件夹的位置,在相应的项目文件夹中找到hooks文件夹.在该文件夹中添加一个post- commit文件:当有commit动作发 ...
- Java线程: 线程调度
线程调度是Java多线程的核心,只有好的调度,才能充分发挥系统的性能,提高程序的执行效率. 一.休眠 休眠的目的是使线程让出CPU的最简单做法,线程休眠的时候,会将CPU交给其他线程,以便轮换执行,休 ...
- 使用jQuery的时候,js文件代码位置规范
/** * 页面加载之后执行的函数:===================================== */$(function() { }); //如果需要同步执行需要同时编写在这里: $( ...
- C# 程序集 和 反射
.Net反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为.Net的反射机 ...
- Eclipse插件手动安装
拷贝至eclipse安装目录/dropins/文件夹下面: 不建议直接拷贝到features/ 和plugins/ 里面:
- 你真的了解如何将 Nginx 配置为Web服务器吗
阅读之前,建议先阅读初识 Nginx. 之后,我们来了解一下 Nginx 配置. 抽象来说,将 Nginx 配置为 Web 服务器就是定义处理哪些 URLS 和如何处理这些URLS 对应的请求.具体来 ...
- windows 下odoo 不同版本安装运行问题
在开发测试不同版本odoo时,总会遇到在同一浏览器下运行出错的状况.虽然可以把不同版本分属不同的端口,但是登录标识会入写用户本地浏览器cookie,由于cookie只匹配域名及路径但是不区分端口, 所 ...
- java_流
转自:http://blog.csdn.net/hguisu/article/details/7418161 1.什么是IO Java中I/O操作主要是指使用Java进行输入,输出操作. Java所有 ...
- 属性——AddComponentMenu
字面理解:添加 组件选项菜单 分析:可能是添加一个脚本或者组件到一个物体上 验证: 新建一个脚本:AttributeTest 提示:添加一个组件菜单属性,允许你放一个脚本在Compoent菜单下,来代 ...
- iOS之网络数据下载和JSON解析
iOS之网络数据下载和JSON解析 简介 在本文中笔者将要给大家介绍IOS中如何利用NSURLconnection从网络上下载数据以及如何解析下载下来的JSON数据格式,以及如何显示数据和托图片的异步 ...