Java 多线程之哪个对象才是锁?
问题背景
在感觉正常的使用ArrayList的迭代删除的操作的时候,发现了如下的崩溃日志:
Caused by: java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.remove(ArrayList.java:846)
产生上述崩溃的代码如下:
Iterator<String> iterator = strs.iterator();
while (iterator.hasNext()) {
String ss = iterator.next();
....
iterator.remove();
}
感觉上述代码是没问题的啊,单线程下我们这么写是没问题的,但是特殊的是此段代码位于一个静态方法里面。没有加同步语句,而且上面还有一个执行添加操作的静态方法,两个方法操作一个静态变量。
看到这里,大概也分析出来了,就是多线程操作同一个变量导致的问题。这时在两个静态方法上面添加synchronized,让它们成为静态同步方法后,问题就解决了。
解决问题时的参考文章:http://www.cnblogs.com/dolphin0520/p/3933551.html
这时如果我们会多想一下,我们使用了锁解决了同步的问题,但是静态同步方法和实例同步方法在锁这块的机制有什么不同呢?
我们都知道当一个线程试图访问同步代码块时,它首先必须得到锁,退出或抛出异常时必须释放锁。这些基础也许大家都知道,但是很多人还是搞不清哪个对象才是锁?如果你能正确回答以下问题,那么才算你彻底搞明白了哪个对象才是锁?
一、静态同步方法问题
如下代码是两个静态同步方法
Class A{
public static synchronized void write(boolean b){
isTrue = b;
}
public static synchronized boolean read(){
return isTrue;
}
}
那么我们来问几个问题
线程1访问A.write(true)方法时,线程2能访问A.read()方法吗?
线程1访问new A().write(false)方法时,线程2能访问new A().read()方法吗?
线程1访问A.write(false)方法时,线程2能访问new A().read()方法吗?
二、实例同步方法问题
如下代码是两个实例同步方法
public synchronized void write(boolean b){
isTrue = b;
}
public synchronized boolean read(){
return isTrue;
}
同样问两个问题:
A a=new A(); 线程1访问a.write(false)方法,线程2能访问a.read()方法吗?
A a=new A(); A b=new A();线程1访问a.write(false)方法,线程2能访问b.read()方法吗?
回答问题之前,先想一下当前方法使用的锁是哪一个?当前线程是否有拿到这把锁?拿到锁了就能访问当前方法了。
三、答案
我们先回顾基础知识,Java中的每一个对象都可以作为锁,而不同的场景锁是不一样的。
对于实例同步方法,锁是当前实例对象。
对于静态同步方法,锁是当前对象的Class对象。
对于同步方法块,锁是Synchonized括号里配置的对象。
线程1访问A.write()方法时,线程2能访问A.read()方法吗?不能,因为静态方法的锁都是A.Class对象,线程1拿到锁之后,线程2就拿不到锁了。
线程1访问new A().write()方法时,线程2能访问new A().read()方法吗?不能,原因同上。
线程1访问A.write()方法时,线程2能访问new A().read()方法吗?不能,原因同上
A a=new A(); 线程1访问a.write()方法,线程2能访问a.read()方法吗?不能,因为这两个方法的锁都是对象a,线程1拿到了锁,线程2就不能访问了。
A a=new A(); A b=new A();线程1访问a.write()方法,线程2能访问b.read()方法吗?可以,因为线程1拿到的是锁是 a,而线程2访问b.read()需要的是锁是b。
现在你应该明白了这句话,对于实例同步方法,锁是当前实例对象。对于静态同步方法,锁是当前对象的Class对象。
四、理解
谈下自己的一些理解,synchronized是针对内存对象加锁,对象头部会标记是否被加锁,在偏向里面还会和线程ID记录下来;
知道几个级别都离不开对象和锁的关系,也就是锁始终在对象上;
常见的无非是:class(class也是一个java对象,只是他相对自己的对象很特殊就是了)、其次是this(也就是new出来的对象)、再其次是自己顶一个Object,当然自己的object如果是static的那也是全局的;
对非静态方法加锁,就是对this加锁,所有相关的this加锁以及非静态方法加锁,都会相互会产生排他;对静态方法加锁,其实就是对class本身进行加锁,所有相关静态方法加锁的以及对class加相互隔离;object就是自定义;
对于方法,如果有方法使用了加锁,未进行加锁的方法不会被隔离;静态方法也是如此;
补充几点:
- 1.对class加锁,父类和子类没有任何关系,也就是对一个parent class加锁,子类的class是不会受到影响的,相反也是如此;
- 2.父类的非静态方法进行了加锁,子类未重写,实例化子类,调用对应方法,也会加锁;通过父类和子类分别实例化的对象没有任何关系,因为是两个对象;
- 3.对静态方法加锁,和对非静态方法加锁,两者不影响,不会相互排斥,因为一个加锁在当前对象上,一个加锁在class对象上,也就是class对象的锁,不会控制对象本身的锁;
- 4.对当前this进行加锁,不会影响类里面定义一个Object的锁,理由同上,不会因为他们有包含关系,导致他们的锁产生嵌套;
控制锁的粒度对编程很重要,这不算技术问题,算是设计,什么是锁的力度呢?
一个人吃饭,占一个座位、还是占一张桌子、还是占一个食堂,是属于范围区域;一个人占着这么大一片区域吃饭后,不走,还要休息,睡觉、干别的,这段时间不让别人进食堂,就是时间区域;两个合并在一起就是粒度;
Java 多线程之哪个对象才是锁?的更多相关文章
- 从火箭发场景来学习Java多线程并发闭锁对象
从火箭发场景来学习Java多线程并发闭锁对象 倒计时器场景 在我们开发过程中,有时候会使用到倒计时计数器.最简单的是:int size = 5; 执行后,size—这种方式来实现.但是在多线程并发的情 ...
- “全栈2019”Java多线程第二十八章:公平锁与非公平锁详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- “全栈2019”Java多线程第十七章:同步锁详解
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Java多线程操作同一个对象,线程不安全
Java多线程操作同一个对象 发现问题:多个线程操作同一资源的情况下,线程不安全,数据紊乱 代码: package multithreading; // Java多线程操作同一个对象 // 买火车票的 ...
- java多线程系列(二)---对象变量并发访问
对象变量的并发访问 前言:本系列将从零开始讲解java多线程相关的技术,内容参考于<java多线程核心技术>与<java并发编程实战>等相关资料,希望站在巨人的肩膀上,再通过我 ...
- Java多线程简析——Synchronized(同步锁)、Lock以及线程池
Java多线程 Java中,可运行的程序都是有一个或多个进程组成.进程则是由多个线程组成的.最简单的一个进程,会包括mian线程以及GC线程. 线程的状态 线程状态由以下一张网上图片来说明: 在图中, ...
- Java多线程学习(六)Lock锁的使用
系列文章传送门: Java多线程学习(二)synchronized关键字(1) Java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Java多 ...
- Java多线程并发05——那么多的锁你都了解了吗
在多线程或高并发情境中,经常会为了保证数据一致性,而引入锁机制,本文将为各位带来有关锁的基本概念讲解.关注我的公众号「Java面典」了解更多 Java 相关知识点. 根据锁的各种特性,可将锁分为以下几 ...
- Java多线程编程核心技术---对象及变量的并发访问(二)
数据类型String的常量池特性 在JVM中具有String常量池缓存的功能. public class Service { public static void print(String str){ ...
随机推荐
- Spring框架系列之AOP思想
微信公众号:compassblog 欢迎关注.转发,互相学习,共同进步! 有任何问题,请后台留言联系! 1.AOP概述 (1).什么是 AOP AOP 为 Aspect Oriented Progra ...
- 如何写出测不出bug的测试用例
我们写测试用例的目的是为了能够整理思路,把要测试的地方列出来,做为知识的积淀,用例可以交给其他测试人员执行,或者是跟需求提出者进行讨论,对用例进行补充和修改. 理论上用例写的越多,越容易发现bug.但 ...
- c++实现一个小算法
题目:有n个格子,每个格子里有坦克,坦克有两滴血,你向格子里投掷炸弹,每次命中坦克他掉一滴血并随机像左或者右移动一个格子,问最少炸几次能把全部坦克炸完. 题解:先向偶数格子投掷炸弹,所有的坦克全跑到奇 ...
- keras初涉笔记【一】
安装keras依赖的库 sudo pip install numpy sudo pip install scipy sudo pip installl pyyaml sudo pipi install ...
- Eventlog控件的使用
CreateEventSource 已重载. 建立一个能够将事件信息写入到系统的特定日志中的应用程序. Delete 已重载. 移除日志资源. DeleteEventSource 已重载. 从事件日志 ...
- UVALive - 3938 (线段树,区间查询)
思路:详细分析见训练指南.注意可能答案的起点在左区间,终点在右区间 AC代码 #include <stdio.h> #include <algorithm> using nam ...
- Java线程编程中isAlive()和join()的使用详解
一个线程如何知道另一线程已经结束?Thread类提供了回答此问题的方法. 有两种方法可以判定一个线程是否结束.第一,可以在线程中调用isAlive().这种方法由Thread定义,它的通常形式如下: ...
- Ubuntu16.04安装搜狗输入法后有黑边问题的解决方法
apt-get install compton compton -b
- ActiveMQ安装及启动
最近项目组遇到MQ的问题(项目组用的IBM MQ),突然想回来看看MQ.本来就不是太熟练,闲来无事写写看看. 不多说,首先是安转ActiveMQ,官方网址:http://activemq.apache ...
- mysql常用基础操作语法(五)--对数据的简单条件查询【命令行模式】
1.单条件查询:select 字段名 from tablename where 条件: 2.简单多条件查询,使用&&或者between and等: 3.is null和is not n ...