synchronized类锁,对象锁,方法锁
synchronized从语法的维度一共有3个用法:
静态方法加上关键字
实例方法(也就是普通方法)加上关键字
方法中使用同步代码块
前两种方式最为偷懒,第三种方式比前两种性能要好。
synchronized从锁的是谁的维度一共有两种情况:
锁住类
锁住对象实例
我们还是从直观的语法结构上来讲述synchronized。
1)静态方法上的锁
静态方法是属于“类”,不属于某个实例,是所有对象实例所共享的方法。也就是说如果在静态方法上加入synchronized,那么它获取的就是这个类的锁,锁住的就是这个类。
2)实例方法(普通方法)上的锁
实例方法并不是类所独有的,每个对象实例独立拥有它,它并不被对象实例所共享。这也比较能推出,在实例方法上加入synchronized,那么它获取的就是这个累的锁,锁住的就是这个对象实例。
那锁住类还是锁住对象实例,这跟我线程安全关系大吗?大,差之毫厘谬以千里的大。为了更好的理解锁住类还是锁住对象实例,在进入“3)方法中使用同步代码块”前,先直观的感受下这两者的区别。
对实例方法(普通方法)上加关键字锁住对象实例锁的解释
首先定义一个Demo类,其中的实例方法加上了synchronized关键字,按照所述也就是说锁住的对象实例。
public class Demo { public synchronized void demo() {
while (true) { //synchronized方法内部是一个死循环,一旦一个线程持有过后就不会释放这个锁
System.out.println(Thread.currentThread());
}
}
}
可以看到在demo方法中定义了一个死循环,一旦一个线程持有这个锁后其他线程就不可能获取这个锁。结合上述synchronized修饰实例方法锁住的是对象实例,如果两个线程针对的是一个对象实例,那么其中一个线程必然不可能获取这个锁;如果两个线程针对的是两个对象实例,那么这两个线程不相关均能获取这个锁。
自定义线程,调用demo方法。
public class MyThread implements Runnable {
private Demo demo; public MyThread(Demo demo) {
this.demo = demo;
} @Override
public void run() {
demo.demo();
}
}
测试程序1:两个线程抢占一个对象实例的锁
public class Main1 {
public static void main(String[] args) {
Demo demo = new Demo();
Thread thread1 = new Thread(new MyThread(demo));
Thread thread2 = new Thread(new MyThread(demo));
thread1.start();
thread2.start();
}
}
如上图所示,输出结果显然只会打印一个线程的信息,另一个线程永远也获取不到这个锁。
测试程序2:两个线程分别抢占两个对象实例的锁
public class Main2 {
public static void main(String[] args) {
Demo demo1 = new Demo();
Demo demo2 = new Demo();
Thread thread1 = new Thread(new MyThread(demo1));
Thread thread2 = new Thread(new MyThread(demo2));
thread1.start();
thread2.start();
}
}
如上图所示,显然,两个线程均进入到了demo方法,也就是均获取到了锁,证明,两个线程抢占的就不是同一个锁,这就是synchronized修饰实例方法时,锁住的是对象实例的解释。
对静态方法上加关键字锁住类锁的解释
静态方法是类所有对象实例所共享的,无论定义多少个实例,是要是静态方法上的锁,它至始至终只有1个。将上面的程序Demo中的方法加上static,无论使用“测试程序1”还是“测试程序2”,均只有一个线程可以抢占到锁,另一个线程仍然是永远无法获取到锁。
让我们重新回到从语法结构上解释synchronized。
3)方法中使用同步代码块
程序的改良优化需要建立在有坚实的基础,如果在不了解其内部机制,改良也仅仅是“形式主义”。
结合开始CodeReview的例子:
你的同事在CodeReview时,要求你将实例方法上的synchronized,改为效率更高的同步代码块方式。在你不清楚同步代码的用法时,网上搜到了一段synchronized(this){}代码,复制下来发现也能用,此时你以为你改良优化了代码。但实际上,你可能只是做了一点形式主义上的优化。
为什么这么说?这需要清楚地认识同步代码块到底应该怎么用。
3.1)synchronized(this){...}
this关键字所代表的意思是该对象实例,换句话说,这种用法synchronized锁住的仍然是对象实例,他和public synchronized void demo(){}可以说仅仅是做了语法上的改变。
public class Demo { public synchronized void demo1() {
while (true) { //死循环目的是为了让线程一直持有该锁
System.out.println(Thread.currentThread());
}
} public synchronized void demo2() {
while (true) {
System.out.println(Thread.currentThread());
}
}
}
改为以下方式:
public class Demo { public void demo1() {
synchronized (this) {
while (true) { //死循环目的是为了让线程一直持有该锁
System.out.println(Thread.currentThread());
}
}
} public void demo2() {
synchronized (this) {
while (true) {
System.out.println(Thread.currentThread());
}
}
}
}
也许后者在JVM中可能会做一些特殊的优化,但从代码分析上来讲,两者并没有做到很大的优化,线程1执行demo1,线程2执行demo2,由于两个方法均是抢占对象实例的锁,只要有一个线程获取到锁,另外一个线程只能阻塞等待,即使两个方法不相关。
3.2)private Object obj = new Object(); synchronized(obj){...}
public class Demo {
private Object lock1 = new Object();
private Object lock2 = new Object(); public void demo1() {
synchronized (lock1) {
while (true) { //死循环目的是为了让线程一直持有该锁
System.out.println(Thread.currentThread());
}
}
} public void demo2() {
synchronized (lock2) {
while (true) {
System.out.println(Thread.currentThread());
}
}
}
}
经过上面的分析,看到这里,你可能会开始懂了,可以看到demo1方法中的同步代码块锁住的是lock1对象实例,demo2方法中的同步代码块锁住的是lock2对象实例。如果线程1执行demo1,线程2执行demo2,由于两个方法抢占的是不同的对象实例锁,也就是说两个线程均能获取到锁执行各自的方法(当然前提是两个方法互不相关,才不会出现逻辑错误)。
3.3)synchronized(Demo.class){...}
这种形式等同于抢占获取类锁,这种方式,同样和3.1一样,收效甚微。
所以CodeReivew后的代码应该是3.2) private Object obj = new Object(); synchronized(obj){...},这才是对你代码的改良优化。
synchronized类锁,对象锁,方法锁的更多相关文章
- [Java]Java入门笔记(三):类、对象和方法
七.类.对象和方法 类和对象的关系 类定义了对象的本质: 类(class)是对象(object)的模板,而对象(object)是类的一个实例(instance). 使多个对象的指向相同: Studen ...
- Python - 类与对象的方法
类与对象的方法
- 新手指引,php什么是常量、变量、数组、类和对象及方法?
众所周知,常量.变量.数组.类和对象及方法共同构成了PHP的基石.那么什么是常量?什么是变量?什么是数组?什么是类和对象及方法?我在此谈谈个人浅见,新手指引,高手勿喷. PHP 常量 定义:常量是单个 ...
- C#类、对象、方法和属性详解
C#类.对象.方法和属性详解 一.相关概念: 1.对象:现实世界中的实体(世间万物皆对象) 2.类:具有相似属性和方法的对象的集合 3.面向对象程序设计的特点:封装 继承 多态 4.对象的三要素:属性 ...
- java 多线程8 : synchronized锁机制 之 方法锁
脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量或者全局静态变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数 ...
- python 类、对象、方法、属性
在python中,一个对象的特征也称为属性(attribute).它所具有的行为也称为方法(method) 结论:对象=属性+方法 在python中,把具有相同属性和方法的对象归为一个类(class) ...
- python中的类,对象,方法,属性等介绍
注:这篇文章写得很好.加底纹的是我自己的理解 python中一切皆为对象,所谓对象:我自己就是一个对象,我玩的电脑就是对象,坐着的椅子就是对象,家里养的小狗也是一个对象...... 我们通过描述属性( ...
- python类、对象、方法、属性之类与对象笔记
python中一切皆为对象,所谓对象:我自己就是一个对象,我玩的电脑就是对象,坐着的椅子就是对象,家里养的小狗也是一个对象...... 我们通过描述属性(特征)和行为来描述一个对象的.比如家里的小狗, ...
- C#程序设计基础——类、对象、方法
类与对象 类 类是一种构造,通过使用该构造,用户可以将其他类型的变量.方法和事件组合在一起,从而创建自定义类型.类就像一个蓝图,它定义类型的数据和行为. 对象 定义类之后,便可通过将类加载到内存中来使 ...
- Objective-C 笔记二 类、对象和方法
对象就是一个物件.面向对象的程序设计可以看成一个物件和你想对它做的事情.这与C语言不同,C语言通常称为过程性语言.在C语言中,通常是先考虑要做什么,然后才关注对象,这几乎总是与面相对象的思考过程相反. ...
随机推荐
- SpringBoot集成RestTemplate
先把原文列出来: springboot实战之常用http客户端整合 springboot2.0集成RestTemplate -----------开始------------ SpringBoot应用 ...
- ActiveMQ基础教程(三):C#连接使用ActiveMQ消息队列
接上一篇:ActiveMQ基础教程(二):安装与配置(单机与集群) 安装部署好集群环境:192.168.209.133:61616,192.168.209.134:61616,192.168.209. ...
- SpringBoot 之 国际化
增加国际化i18n语言配置: # src/main/resources/i18n/login.properties login.btn=登录 # src/main/resources/i18n/log ...
- .NET对接极光消息推送
什么是APP消息推送? 很多手机APP会不定时的给用户推送消息,例如一些新闻APP会给用户推送用户可能感兴趣的新闻,或者APP有更新了,会给用户推送是否选择更新的消息等等,这就是所谓的"消息 ...
- 《剑指offer》面试题09. 用两个栈实现队列
问题描述 用两个栈实现一个队列.队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能.(若队列中没有元素,dele ...
- 《剑指offer》面试题53 - II. 0~n-1中缺失的数字
问题描述 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0-n-1之内.在范围0-n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字. 示例 1: 输入: [ ...
- [开发笔记usbTOcan]需求获取与系统需求分析
简介 一直一以来都是站在实现某个模块功能的角度去做软件开发,没有尝试过站在系统的层面去做开发.最近正好不忙,觉得是时候以系统工程师的视角,去开发一个完整的系统.接下来的几篇文章,我会记录一个USB转C ...
- 【记录一个问题】一个golang中的BUG,为啥编译的时候无法发现,而单独跑测试用例就发现了
代码大致如下: func DoSomething(){ log.Printf("a=%s, b=%s, c=%s", a, b) //忘记少写一个参数.但是编译正常通过 } fun ...
- ROS Kinetic 使用PocketSphinx进行语音识别报错
已解决,重启电脑,装环境把声卡驱动那些似乎死机了 ROS Kinetic 使用PocketSphinx进行语音识别教程 https://www.ncnynl.com/archives/201701/ ...
- 图文并茂理解iptables
原文地址:http://www.zsythink.net/archives/1199 以下是转载内容: iptables详解:图文并茂理解iptables | 朱双印博客 这篇文章会尽量以通俗易懂的方 ...