java的多线程安全,ReentrantLock与synchronized锁
前言##
多线程总的来说是一个很大的模块,所以虽然之前就想写但一直感觉有地方没有理解透,在经过了一段时间学习后,终于有点感觉了,在此写下随笔。
多线程安全问题##:
首先和大家讨论一下多线程为什么会不安全,大家先看下面的程序。
/**
- @author lw
*/
public class Test extends Thread{
public void run()
{
for(int i=1;i<=10;i++)
{
System.out.println(i);
}
}
public static void main(String args[])
{
Test t1=new Test();
Test t2=new Test();
Test t3=new Test();
Test t4=new Test();
t1.start();
t2.start();
t3.start();
t4.start();
}
上面这段程序大致意思就是新建了四个线程,每个线程的操作都是输出1-10,按说来应该按线程启动顺序依次输出,但其实并不是。<--12345678112345678910234567891091012345678910-->这是输出的结果。线程并没有顺序执行,原因就是线程的抢占。在线程一执行到一半,输出到8的时候,便被其他线程抢占,其他线程继续输出。
这样的并发会带来什么问题呢?大家请看下面这段代码。
/**
- @author lw
- */
public class Test extends Thread{
static int temp=1;
public void run()
{
temp++;
System.out.println(temp);
}
public static void main(String args[])
{
Test t1=new Test();
Test t2=new Test();
Test t3=new Test();
Test t4=new Test();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
大家可以上面的程序大致是定义了一个static的整形变量,然后每一个线程可以对这个变量加1,
首先static变量是全局共享的,每一个线程都能操作这个变量,问题就出在这里。如果有一次线程1运行,然后读入了该变量为1,这个时候线程2抢占,然后对该变量进行了加一的操作,此时线程1再继续运行,但该变量现在已经是2了,线程1读入的确是之前的1,在加一之后为2,就出问题了。这种问题我们称为线程之间不同步,因为线程之间的操作互相是不可见的。下面,我们深入讨论一下为什么会这样。
线程不同步的原因
线程之所以会不同步,本质原因在于每个线程的高速缓存区。每个线程在创建后会有自己的一个缓存区,在线程要访问主存中的变量的时候会先将主存中的变量加入缓存,然后进行操作,这样可以避免主存访问过于频繁,可以加快线程的执行效率(类似于cache)。但问题在于每个线程的缓存区之间不可见,如果载入的是主存中的同一个变量,分别进行了更改,就会出现线程不同步的问题。
不同步解决策略
好了,上面都是铺垫,下面才是重点,如何解决线程不同步问题。java给出了锁的概念。所谓锁形象一点理解就是一个线程在用一个资源就像一个人进了一扇门,如果不锁门,其他人也会进来,但如果加了锁,就意味着这个资源被这个线程独占,而且必须要退出了才能被其他线程使用。我们常用的就是synchronized锁,又叫同步锁。我们先看一下这个锁的效果。
/**
* @author lw
*
*/
public class Test extends Thread{
String lo="";
public void run()
{
synchronized(lo)
{
for(int i=1;i<=10;i++)
{
System.out.println(i);
}
}
}
public static void main(String args[])
{
Test t1=new Test();
Test t2=new Test();
Test t3=new Test();
Test t4=new Test();
t1.start();
t2.start();
t3.start()
t4.start();
}
}
在经过了上面的同步之后,线程便可以按顺序运行,因为在第一个线程开始后,他会获得变量lo的锁,然后执行下面的代码块,其他线程在得到这个锁之前会处于一个阻塞状态,等待第一个线程释放锁之后其他线程竞争,然后获得锁的线程继续代码块里的操作,这样就可以保证线程之间的异步了,接下来我们需要知道的是为什么加了锁可以实现同步。
synchronized是如何实现同步的##
好吧其实很简单,比较机智的读者可能已经猜到了,他其实是使各个线程之间的高速缓存区失效了,然后线程要获取该变量的时候需要在主存中读写,这个时候对该变量的操作对于各个线程之间是可见的,然后操作结束之后再刷新其缓存区,哈哈哈是不是很简单。。。
synchronized需注意的事项
大家要注意的是synchronized加锁的目标是对象,并不是代码块。这是初学者容易进入的误区。有人认为只要是synchronized里面的操作一定不会有问题, 但当这样想的时候其实你已经凉了。请看下面的代码。
/**
* @author lw
*
*/
public class Test extends Thread{
String lo=new String();
public void run()
{
synchronized(lo)
{
for(int i=1;i<=10;i++)
{
System.out.println(i);
}
}
}
public static void main(String args[])
{
Test t1=new Test();
Test t2=new Test();
Test t3=new Test();
Test t4=new Test();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
看上去与上面的没太大差别,但细心的读者会发现有一行变成了String lo=new String();这个时候的锁便没有任何意义,因为这个对象每一个线程都会new一个,也就是说每一个线程都会获得一个,所以完全不起作用。可能基础欠佳的同学会问之前的String lo=“”;为什么可以,因为每一个lo都会指向常量池(常量池这里不展开讲了 ,手要废了。。不知道的可以百度一下)中的同一个对象,所以每一个线程的还都是指向同一段主存,锁就会起作用。大家是不是觉得synchonized已经很完美了,no no no还有更完美的,rentrantlock闪亮登场!!!(打字好累啊。。。。#==)
reentrantlock
首先我们讨论一下synchonized的缺点。一是不灵活,synchonized在锁定之后必须要代码块结束之后才能释放锁,然后被其他线程获得。那么如果获取到锁的这个线程要执行非常长的时间呢,那其他的线程不是会一直阻塞在这里,这时如果有哪个线程生气了不想等了怎么办?抱歉不可以,需要一直等待。另一方面,同步锁的释放顺序也很固定,必须是加锁的反顺序,很不潇洒等等。。。但我们的reentrantlock就不一样了,话不多说先看代码。
/**
* @author lw
*
*/
public class Test extends Thread{
private static ReentrantLock lock =new ReentrantLock();
public void run()
{
try{
lock.lock();
for(int i=1;i<=10;i++)
{
System.out.println(i);
}
}
finally
{
lock.unlock();
}
}
public static void main(String args[])
{
Test t1=new Test();
Test t2=new Test();
Test t3=new Test();
Test t4=new Test();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
上面我们可以看到声明了ReentrantLock对象后只需调用其中的lock方法便可直接加锁,而释放锁需要unlock方法。这样一是很灵活,不需要代码块结束再释放,还有就是 ReentrantLock是可中断的,如果等待的线程不想等了,好说,interrupt掉就好了,另外, ReentrantLock可以设为悲观锁和乐观锁,而synchonized则默认为悲观锁,不可改变,不够灵活。所以综上,ReentrantLock更加灵活多变。但大家在使用时一定要记得unlock,最好写在finally里面防止忘记,不然就会造成其他线程阻塞。
多线程是一个很大的知识块,以上是笔者自己学习思考后的总结归纳,还有很多没有涉及到,另外分享内容如有不当之处望大家多多指正,共同进步~
下期预告###
radius缓存
java的多线程安全,ReentrantLock与synchronized锁的更多相关文章
- 死磕 java同步系列之ReentrantLock VS synchronized——结果可能跟你想的不一样
问题 (1)ReentrantLock有哪些优点? (2)ReentrantLock有哪些缺点? (3)ReentrantLock是否可以完全替代synchronized? 简介 synchroniz ...
- Java基础-多线程-③线程同步之synchronized
使用线程同步解决多线程安全问题 上一篇 Java基础-多线程-②多线程的安全问题 中我们说到多线程可能引发的安全问题,原因在于多个线程共享了数据,且一个线程在操作(多为写操作)数据的过程中,另一个线程 ...
- java并发系列(三)-----ReentrantLock(重入锁)功能详解和应用演示
1. ReentrantLock简介 jdk中独占锁的实现除了使用关键字synchronized外,还可以使用ReentrantLock.虽然在性能上ReentrantLock和synchronize ...
- Java多线程(九) synchronized 锁对象的改变
public class MyService { private String lock = "123"; public void testMethod() { synchroni ...
- Java多线程之ReentrantLock重入锁简介与使用教程
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6543947.html 我们知道,线程安全问题需要通过线程之间的同步来解决,而同步大多使用syncrhoize ...
- Java中的ReentrantLock和synchronized两种锁机制的对比
原文:http://www.ibm.com/developerworks/cn/java/j-jtp10264/index.html 多线程和并发性并不是什么新内容,但是 Java 语言设计中的创新之 ...
- Java:多线程,线程同步,同步锁(Lock)的使用(ReentrantLock、ReentrantReadWriteLock)
关于线程的同步,可以使用synchronized关键字,或者是使用JDK 5中提供的java.util.concurrent.lock包中的Lock对象.本文探讨Lock对象. synchronize ...
- Java多线程4:synchronized锁机制
脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过 ...
- Java多线程6:Synchronized锁代码块(this和任意对象)
一.Synchronized(this)锁代码块 用关键字synchronized修饰方法在有些情况下是有弊端的,若是执行该方法所需的时间比较长,线程1执行该方法的时候,线程2就必须等待.这种情况下就 ...
随机推荐
- Eclipse中的maven项目打war包
在对应的pom文件中,找到packing这个属性,改为war:如果没有,就自己加一个,这个是有默认值的,默认为jar. 例如: <modelVersion>4.0.0</modelV ...
- zufeoj 数值排序(简单题)
数值排序 时间限制: 1 Sec 内存限制: 128 MB提交: 186 解决: 45[提交][状态][讨论版] 题目描述 输入一串数字,把这串数字中的‘0’都看成空格,那么就得到一行用空格分割的 ...
- 实例甜点 Unreal Engine 4迷你教程(3)之用C++改变Image小部件的其它属性
完成本迷你教程之前,请前往完成以下迷你教程: ·实例甜点 Unreal Engine 4迷你教程(2)之用C++改变Image小部件的颜色: 在上一次的迷你教程的LearnWidgets工程上进行(如 ...
- Linux学习笔记 -- 文本编辑器之 vi与vim
vi/vim 的使用 基本上 vi/vim 共分为三种模式,分别是命令模式(Command mode),插入模式(Insert mode)和底线命令模式(Last line mode). 这三种模式的 ...
- jQueryUI Sortable 应用Demo
最近工作用需要设计一个自由布局的页面设计.我选了jQuery UI 的 sortable ,可以拖拽,自由排序 使用很方便,写一个demo,做个记录. 第一.单项目自由排序 下图效果 代码段: < ...
- 使用 Ansible 管理 MySQL 复制
Ansible 是一个新兴的 IT 自动化工具.本文将介绍如何通过 Ansible 配置及管理 MySQL 主.从复制环境,实现部署过程自动化,体验 Ansible 简单快速带来的快感. 简介: An ...
- Maven+Mybatis+Spring+SpringMVC实现分页查询
转载:http://www.cnblogs.com/zhangtan/p/5846955.html 一.项目搭建 关于项目搭建,小宝鸽以前写过一篇Spirng+SpringMVC+Maven+Myba ...
- Java微信公众平台开发(九)--关键字回复以及客服接口实现(该公众号暂时无法提供服务解决方案)
转自:http://www.cuiyongzhi.com/post/47.html 我们在微信公众号的后台可以发现微信给我们制定了两种模式,一种是开发者模式(也就是我们一直在做的开发),还有一种模式是 ...
- No mapping found for HTTP request with URI [/jiaoyu/student/add] in DispatcherServlet with name 'SpringMVC'
项目是使用spring MVC (1)在浏览器中访问,后台总报错: No mapping found for HTTP request with URI [/exam3/welcome] in Dis ...
- [原创]Spring JdbcTemplate 使用总结与经验分享
引言 近期开发的几个项目,均是基于Spring boot框架的web后端项目,使用JdbcTemplate执行数据库操作,实际开发过程中,掌握了一些有效的开发经验,踩过一些坑,在此做个记录及总结,与各 ...