Java并发编程(四)synchronized
一.synchronized同步方法或者同步块
在了解synchronized关键字的使用方法之前,我们先来看一个概念:互斥锁,顾名思义:能到达到互斥访问目的的锁。
举个简单的例子:如果对临界资源加上互斥锁,当一个线程在访问该临界资源时,其他线程便只能等待。
在Java中,每一个对象都拥有一个锁标记(monitor),也称为监视器,多线程同时访问某个对象时,线程只有获取了该对象的锁才能访问。
在Java中,可以使用synchronized关键字来标记一个方法或者代码块,当某个线程调用该对象的synchronized方法或者访问synchronized代码块时,这个线程便获得了该对象的锁,其他线程暂时无法访问这个方法,只有等待这个方法执行完毕或者代码块执行完毕,这个线程才会释放该对象的锁,其他线程才能执行这个方法或者代码块。
下面通过几个简单的例子来说明synchronized关键字的使用:
1.synchronized方法
下面这段代码中两个线程分别调用insertData对象插入数据:
- public class Test {
- public static void main(String[] args) {
- final InsertData insertData = new InsertData();
- new Thread() {
- public void run() {
- insertData.insert(Thread.currentThread());
- };
- }.start();
- new Thread() {
- public void run() {
- insertData.insert(Thread.currentThread());
- };
- }.start();
- }
- }
- class InsertData {
- private ArrayList<Integer> arrayList = new ArrayList<Integer>();
- public synchronized void insert(Thread thread){
- for(int i=;i<;i++){
- System.out.println(thread.getName()+"在插入数据"+i);
- arrayList.add(i);
- }
- }
- }
从上输出结果说明,Thread-1插入数据是等Thread-0插入完数据之后才进行的。说明Thread-0和Thread-1是顺序执行insert方法的。
这就是synchronized方法。
不过有几点需要注意:
1)当一个线程正在访问一个对象的synchronized方法,那么其他线程不能访问该对象的其他synchronized方法。这个原因很简单,因为一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象的锁,所以无法访问该对象的其他synchronized方法。
2)当一个线程正在访问一个对象的synchronized方法,那么其他线程能访问该对象的非synchronized方法。这个原因很简单,访问非synchronized方法不需要获得该对象的锁,假如一个方法没用synchronized关键字修饰,说明它不会使用到临界资源,那么其他线程是可以访问这个方法的,
3)如果一个线程A需要访问对象object1的synchronized方法fun1,另外一个线程B需要访问对象object2的synchronized方法fun1,即使object1和object2是同一类型,也不会产生线程安全问题,因为他们访问的是不同的对象,所以不存在互斥问题。
2.synchronized代码块
当在某个线程中执行这段代码块,该线程会获取对象synObject的锁,从而使得其他线程无法同时访问该代码块。
synObject可以是this,代表获取当前对象的锁,也可以是类中的一个属性,代表获取该属性的锁。
- 比如上面的insert方法可以改成以下两种形式:
- class InsertData {
- private ArrayList<Integer> arrayList = new ArrayList<Integer>();
- public void insert(Thread thread){
- synchronized (this) {
- for(int i=;i<;i++){
- System.out.println(thread.getName()+"在插入数据"+i);
- arrayList.add(i);
- }
- }
- }
- }
- class InsertData {
- private ArrayList<Integer> arrayList = new ArrayList<Integer>();
- private Object object = new Object();
- public void insert(Thread thread){
- synchronized (object) {
- for(int i=;i<;i++){
- System.out.println(thread.getName()+"在插入数据"+i);
- arrayList.add(i);
- }
- }
- }
- }
- 从上面可以看出,synchronized代码块使用起来比synchronized方法要灵活得多。因为也许一个方法中只有一部分代码只需要同步,如果此时对整个方法用synchronized进行同步,会影响程序执行效率。而使用synchronized代码块就可以避免这个问题,synchronized代码块可以实现只对需要同步的地方进行同步
另外,每个类也会有一个锁,它可以用来控制对static数据成员的并发访问。
并且如果一个线程执行一个对象的非static synchronized方法,另外一个线程需要执行这个对象所属类的static synchronized方法,此时不会发生互斥现象,因为访问static synchronized方法占用的是类锁,而访问非static synchronized方法占用的是对象锁,所以不存在互斥现象。(对象级别的synchronized,还有类级别的synchronized.锁定的是整 个clz.class)
- 看下面这段代码就明白了:
- public class Test {
- public static void main(String[] args) {
- final InsertData insertData = new InsertData();
- new Thread(){
- @Override
- public void run() {
- insertData.insert();
- }
- }.start();
- new Thread(){
- @Override
- public void run() {
- insertData.insert1();
- }
- }.start();
- }
- }
- class InsertData {
- public synchronized void insert(){
- System.out.println("执行insert");
- try {
- Thread.sleep();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("执行insert完毕");
- }
- public synchronized static void insert1() {
- System.out.println("执行insert1");
- System.out.println("执行insert1完毕");
- }
- }
执行结果;
第一个线程里面执行的是insert方法,不会导致第二个线程执行insert1方法发生阻塞现象。
下面我们看一下synchronized关键字到底做了什么事情,我们来反编译它的字节码看一下,下面这段代码反编译后的字节码为:
- public class InsertData {
- private Object object = new Object();
- public void insert(Thread thread){
- synchronized (object) {
- }
- }
- public synchronized void insert1(Thread thread){
- }
- public void insert2(Thread thread){
- }
- }
从反编译获得的字节码可以看出,synchronized代码块实际上多了monitorenter和monitorexit两条指令。monitorenter指令执行时会让对象的锁计数加1,而monitorexit指令执行时会让对象的锁计数减1,其实这个与操作系统里面的PV操作很像,操作系统里面的PV操作就是用来控制多个线程对临界资源的访问。对于synchronized方法,执行中的线程识别该方法的 method_info 结构是否有 ACC_SYNCHRONIZED 标记设置,然后它自动获取对象的锁,调用方法,最后释放锁。如果有异常发生,线程自动释放锁。
有一点要注意:对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。
二.java单例模式(双重检查加锁)的原因
双重检查加锁,只有在第一次实例化时,才启用同步机制,提高了性能。
- public class Singleton{
- private static Singleton instance = null;//是否是final的不重要,因为最多只可能实例化一次。
- private Singleton(){}
- public static Singleton getInstance(){
- if(instance == null){
- //双重检查加锁,只有在第一次实例化时,才启用同步机制,提高了性能。
- synchronized(Singleton.Class){
- if(instance == null){
- instance = new Singleton();
- }
- }
- }
- return instance;
- }
Java并发编程(四)synchronized的更多相关文章
- Java并发编程:Synchronized及其实现原理
Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...
- Java并发编程:Synchronized底层优化(偏向锁、轻量级锁)
Java并发编程系列: Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) Java 并发编程 ...
- Java并发编程:synchronized
Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...
- 【转】Java并发编程:synchronized
一.什么时候会出现线程安全问题? 在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个资源的情况,这种资源可以是各种类型的资源:一个变量.一个对象.一个文件.一个数据库表等,而 ...
- 4、Java并发编程:synchronized
Java并发编程:synchronized 虽然多线程编程极大地提高了效率,但是也会带来一定的隐患.比如说两个线程同时往一个数据库表中插入不重复的数据,就可能会导致数据库中插入了相同的数据.今天我们就 ...
- 【转】Java并发编程:Synchronized及其实现原理
一.Synchronized的基本使用 Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法.Synchronized的作用主要有三个:(1)确保线程互斥的访问同步 ...
- Java 并发编程(四):如何保证对象的线程安全性
01.前言 先让我吐一句肺腑之言吧,不说出来会憋出内伤的.<Java 并发编程实战>这本书太特么枯燥了,尽管它被奉为并发编程当中的经典之作,但我还是忍不住.因为第四章"对象的组合 ...
- 【Java并发编程】synchronized相关面试题总结
目录 说说自己对于synchronized关键字的了解 synchronized关键字的三种使用 synchronized关键字的底层原理 JDK1.6之后对synchronized关键字进行的优化 ...
- 【Java并发编程四】关卡
一.什么是关卡? 关卡类似于闭锁,它们都能阻塞一组线程,直到某些事件发生. 关卡和闭锁关键的不同在于,所有线程必须同时到达关卡点,才能继续处理.闭锁等待的是事件,关卡等待的是其他线程. 二.Cycli ...
- Java并发编程实例(synchronized)
此处用一个小程序来说明一下,逻辑是一个计数器(int i):主要的逻辑功能是,如果同步监视了资源i,则不输出i的值,但如果没有添加关键字synchronized,因为是两个线程并发执行,所以会输出i的 ...
随机推荐
- requsets模块的学习
requests模块的学习 使用之前 pip install requests 发起get,post,请求获取响应 response = requests.get(url,headers) # 发起g ...
- 从github clone文件: Failed to receive SOCKS4 connect request ack.
安装了代理,能上网,也能从github上下载文件,就是无法从github上clone文件, 查了很久资料后,终于发现使用sudo可以解决问题.不过,不知道原因是什么? 比如:git clone htt ...
- 外媒:比特币大陆将于9月IPO 规模或高达180亿美元
看看你们坚持买的比特币是否值得? 北京时间8月13日上午消息,据CoinDesk获得的文件,比特币大陆将于今年9月申请首次公开募股(IPO),其规模可能高达180亿美元,市值预计在400亿美元到500 ...
- 【LeetCode】7. 整数反转python3
题目 给出一个 32 位的有符号整数,你需要将这个整数中每位上的数字进行反转. 示例 1:输入: 123 输出: 321 示例 2:输入: -123 输出: -321 示例 3:输入: 120 输出 ...
- .net core内部分享ppt
Microsoft .NET 自 2002 年发行 v1.0 以来,已经过了近 14 个年头,在这 14 年里面,.NET 日渐成熟并成为 Microsoft 的重要开发平台之一,只要是在 Windo ...
- TCP/IP 笔记 - 域名解析和域名系统
由于IP地址的烦琐导致的记忆和使用困难,互联网支持使用主机名称来识别包括客户机和服务器在内的主机.同时为了使用一系列协议,主机名称通过称为"名称解析"的过程转换成对应IP地址. 互 ...
- PostgreSQL 数据类型
数值类型 数值类型由两个字节,4字节和8字节的整数,4字节和8字节的浮点数和可选精度的小数.下表列出了可用的类型. www.yiibai.com Name Storage Size Descripti ...
- JS nodeList转数组,兼容IE低版本
一.前言 nodeList转数组貌似很少会这样去操作,但我在做图片懒加载时,我获取了所有需要做懒加载的img元素,也就是一个NodeList对象,打个比方: 对这些元素进行src修改后,我想将此项从N ...
- git第七节---git merge和git rebase
# git merge和git rebase 都可以进行分支合并 #git merge 合并后保留记录两个分支的记录 #git rebase合并后会展示成一个分支的记录,另一个分支的提交实际生成了一个 ...
- 史上最全的 Python 3 类型转换指南
int 支持转换为 int 类型的,仅有 float.str.bytes,其他类型均不支持. float -> int 会去掉小数点及后面的数值,仅保留整数部分. int(-12.94) # - ...