锁之“重量级锁”Synchronized
目录:
Synchronized作用
1、Synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法或者代码块。
2、同步的作用不仅仅是互斥,它的另一个作用就是共享可变性,当某个线程修改了可变数据并释放锁后,其它线程可以获取被修改变量的最新值。如果没有正确的同步,这种修改对其它线程是不可见的。
一、Synchronized的基本使用
Synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。Synchronized的作用主要有三个:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改能够及时可见(3)有效解决重排序问题。从语法上讲,Synchronized总共有四种不同的同步块:
1.同步的实例方法(锁用的是其实例对象本身。所有的非静态同步方法执行需要顺序执行,即不能并行执行。)
2.同步的静态方法(锁用的是其类对象本身。所有的静态同步方法执行需要顺序执行,即不能并行执行。)
3.实例方法中的同步块(锁是自己指定的,但不能是引用性对象及null对象)
4.静态方法中的同步块(锁是自己指定的,但不能是引用性对象及null对象)
静态同步方法与非静态同步方法区别:
所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
而所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!
同步块:
而对于同步块,由于其锁是可以选择的,所以只有使用同一把锁的同步块之间才有着竞态条件,这就得具体情况具体分析了,但这里有个需要注意的地方,同步块的锁是可以选择的,但是不是可以任意选择的!!!!这里必须要注意一个物理对象和一个引用对象的实例变量之间的区别!使用一个引用对象的实例变量作为锁并不是一个好的选择,因为同步块在执行过程中可能会改变它的值,其中就包括将其设置为null,而对一个null对象加锁会产生异常,并且对不同的对象加锁也违背了同步的初衷!这看起来是很清楚的,但是一个经常发生的错误就是选用了错误的锁对象,因此必须注意:同步是基于实际对象而不是对象引用的!多个变量可以引用同一个对象,变量也可以改变其值从而指向其他的对象,因此,当选择一个对象锁时,我们要根据实际对象而不是其引用来考虑!作为一个原则,不要选择一个可能会在锁的作用域中改变值的实例变量作为锁对象!
1)执行完同步代码块,就会释放锁。(synchronized)
2)在执行同步代码块的过程中,遇到异常而导致线程终止,锁也会被释放。(exception)
3)在执行同步代码块的过程中,执行了锁所属对象的wait()方法,这个线程会释放锁,进入对象的等待池。(wait)
从上面的三点我就可以看到stop方法释放锁是在第二点的,通过抛出异常来释放锁,通过证明,这种方式是不安全的,不可靠的。
接下来我就通过几个例子程序来说明一下这三种使用方式(为了便于比较,三段代码除了Synchronized的使用方式不同以外,其他基本保持一致)。
1.1、没有同步的情况:
package com.dxz.synchronize; public class SynchronizedTest {
public void method1() {
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
} public void method2() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
} public static void main(String[] args) {
final SynchronizedTest test = new SynchronizedTest(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
}
}
执行结果如下,线程1和线程2同时进入执行状态,线程2执行速度比线程1快,所以线程2先执行完成,这个过程中线程1和线程2是同时执行的。
thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
thread1 Method 1 end
1.2、对普通方法同步:
package com.dxz.synchronize; public class SynchronizedTest2 {
public synchronized void method1() {
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
} public synchronized void method2() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
} public void method3() {
System.out.println(Thread.currentThread().getName() + " Method 3 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 3 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 3 end");
} public static void main(String[] args) {
//test2demo1();
//test2demo2();
test2demo3();
}
private static void test2demo1() {
final SynchronizedTest2 test = new SynchronizedTest2(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
}
private static void test2demo2() {
final SynchronizedTest2 test = new SynchronizedTest2();
final SynchronizedTest2 test2 = new SynchronizedTest2(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test2.method2();
}
},"thread2").start();
} private static void test2demo3() {
final SynchronizedTest2 test = new SynchronizedTest2(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method3();
}
},"thread2").start();
}
}
test2demo1的执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread1 Method 1 end
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
test2demo2的执行结果:
thread2 Method 2 start
thread2 Method 2 execute
thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 end
thread1 Method 1 end
test2demo3的执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 3 start
thread2 Method 3 execute
thread2 Method 3 end
thread1 Method 1 end
2.1、test2demo2测试方法中说明:test和test2属于不同的对象,所以同步互不干扰,线程1和线程2会并行执行。
2.2、通过test2demo1与test2demo3对比说明:同一个类中的所有synchronized修饰的方法是不能同时调用的,也就是说同时只能调用其中一个方法,比如线程1调用method1方法,在method1方法执行完之前,线程2调用method2方法,这个时候线程2就会阻塞,直到线程1调用完method1方法后,线程2才开始执行method2方法。(不仅仅是多个线程调用同一个同步方法)。
2.3、如果线程拥有同步和非同步方法,则非同步方法可以被多个线程自由访问而不受锁的限制。
3、静态方法(类)同步
package com.dxz.synchronize; public class SynchronizedTest3 {
public static synchronized void method1() {
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
} public static synchronized void method2() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
} public synchronized void method3() {
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
} public static void main(String[] args) {
//test3demo1();
//test3demo2();
test3demo3();
} private static void test3demo1() {
final SynchronizedTest3 test = new SynchronizedTest3(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
} private static void test3demo2() {
final SynchronizedTest3 test = new SynchronizedTest3();
final SynchronizedTest3 test2 = new SynchronizedTest3(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test2.method2();
}
},"thread2").start();
} private static void test3demo3() {
final SynchronizedTest3 test = new SynchronizedTest3(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method3();
}
},"thread2").start();
}
}
test3demo1执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread1 Method 1 end
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
test3demo2执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread1 Method 1 end
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
test3demo3执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 start
thread2 Method 2 execute
thread2 Method 2 end
thread1 Method 1 end
3.1、test3demo1与test3demo2对比:对静态方法的同步本质上是对类的同步(静态方法本质上是属于类的方法,而不是对象上的方法),所以即使test和test2属于不同的对象,但是它们都属于SynchronizedTest类的实例,所以也只能顺序的执行method1和method2,不能并发执行。
3.2、test3demo2与test3demo3对比说明:实例对象上的锁与类对象的锁是两个,所以可以并行的执行method1和method3。
4、代码块同步
package com.dxz.synchronize; public class SynchronizedTest4 {
public void method1(){
System.out.println(Thread.currentThread().getName() + " Method 1 start");
try {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " Method 1 execute");
Thread.sleep(3000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 1 end");
} public void method2(){
System.out.println(Thread.currentThread().getName() + " Method 2 start");
try {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " Method 2 execute");
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " Method 2 end");
} public static void main(String[] args) {
test4demo1();
} private static void test4demo1() {
final SynchronizedTest4 test = new SynchronizedTest4(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test.method2();
}
},"thread2").start();
} private static void test4demo2() {
final SynchronizedTest4 test = new SynchronizedTest4();
final SynchronizedTest4 test2 = new SynchronizedTest4(); new Thread(new Runnable() {
@Override
public void run() {
test.method1();
}
},"thread1").start(); new Thread(new Runnable() {
@Override
public void run() {
test2.method2();
}
},"thread2").start();
}
}
test4demo1执行结果:
thread1 Method 1 start
thread1 Method 1 execute
thread2 Method 2 start
thread1 Method 1 end
thread2 Method 2 execute
thread2 Method 2 end
test4demo2执行结果:
thread1 Method 1 start
thread2 Method 2 start
thread2 Method 2 execute
thread1 Method 1 execute
thread2 Method 2 end
thread1 Method 1 end
4.1、虽然线程1和线程2都进入了对应的方法开始执行,但是线程2在进入同步块之前,需要等待线程1中同步块执行完成。
锁之“重量级锁”Synchronized的更多相关文章
- 线程安全(中)--彻底搞懂synchronized(从偏向锁到重量级锁)
接触过线程安全的同学想必都使用过synchronized这个关键字,在java同步代码快中,synchronized的使用方式无非有两个: 通过对一个对象进行加锁来实现同步,如下面代码. synchr ...
- synchronized底层实现原理&CAS操作&偏向锁、轻量级锁,重量级锁、自旋锁、自适应自旋锁、锁消除、锁粗化
进入时:monitorenter 每个对象有一个监视器锁(monitor).当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:1 ...
- java 偏向锁、轻量级锁及重量级锁synchronized原理
Java对象头与Monitor java对象头是实现synchronized的锁对象的基础,synchronized使用的锁对象是存储在Java对象头里的. 对象头包含两部分:Mark Word 和 ...
- 【锁】synchronized的实现(偏向锁、轻量级锁、重量级锁)
synchronized的三种应用方式 一. 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁. 二. 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁. 三. ...
- 关于Synchronized的偏向锁,轻量级锁,重量级锁,锁升级过程,自旋优化,你该了解这些
前言 相信大部分开发人员,或多或少都看过或写过并发编程的代码.并发关键字除了Synchronized(如有不懂请移至传送门,关于Synchronized的偏向锁,轻量级锁,重量级锁,锁升级过程,自旋优 ...
- synchronized和 synchronized 了解偏向锁、轻量级锁、重量级锁的概念以及升级机制、以及和ReentrantLock的区别。
并发 synchronized 了解偏向锁.轻量级锁.重量级锁的概念以及升级机制.以及和ReentrantLock的区别. https://www.cnblogs.com/deltadeb ...
- JVM锁简介:偏向锁、轻量级锁和重量级锁
转自:https://www.aimoon.site/blog/2018/05/21/biased-locking/ 比较复杂,简略见另一篇:https://www.cnblogs.com/twohe ...
- Java 多线程之:偏向锁,轻量级锁,重量级锁
一:java多线程互斥,和java多线程引入偏向锁和轻量级锁的原因? --->synchronized的重量级别的锁,就是在线程运行到该代码块的时候,让程序的运行级别从用户态切换到内核态,把所有 ...
- JAVA对象分析之偏向锁、轻量级锁、重量级锁升级过程
在HotSpot虚拟机里,对象在堆内存中的存储布局可以划分为三个部分: 对象头(Header) 实例数据(Instance Data) 对齐填充(Padding). 对象头 HotSpot虚拟机(后面 ...
- jvm锁的四种状态 无锁状态 偏向锁状态 轻量级锁状态 重量级锁状态
一:java多线程互斥,和java多线程引入偏向锁和轻量级锁的原因? --->synchronized是在jvm层面实现同步的一种机制. jvm规范中可以看到synchronized在jvm里 ...
随机推荐
- JavaWeb笔记——Jsp的指令、内置对象和动作标签
JSP三大指令 一个jsp页面中,可以有0~N个指令的定义! 1. page --> 最复杂:<%@page language="java" info=" ...
- http://blog.csdn.net/xiamizy/article/details/40781939
http://blog.csdn.net/xiamizy/article/details/40781939
- hadoop中的ssh无密码登录配置
在配置Hadoop集群分布时,要使用SSH免密码登录,假设现在有两台机器hadoop@Master(192.168.1.101),作为Master机,hadoop@Slave(192.168.1.10 ...
- PHP Session可能会引起并发问题
在进行Web应用程序开发的时候,人们经常会用Session存储数据.但可能有人不知道,在PHP中,Session使用不当可能会引起并发问题.印度医疗行业软件解决方案提供商Plus91 Technolo ...
- 《Linux内核设计与实现》读书笔记(十)- 内核同步方法【转】
转自:http://www.cnblogs.com/wang_yb/archive/2013/05/01/3052865.html 内核中提供了多种方法来防止竞争条件,理解了这些方法的使用场景有助于我 ...
- jsonp获取服务器数据的方式
jsonp获取服务器的数据,有两种 一,跨域 二,不跨域 如果跨域 js的写法有两种 1, <script type="text/javascript"> $(func ...
- URAL1352. Mersenne Primes
梅森素数 打表 搜梅森素数的时候 看到一句话 欧拉在双目失明的情况下 用心算出了2的31次方-1是素数 他用心算的... #include <iostream> #include<c ...
- Git基础(三)
本章 就开始和大家一起学习第三块内容:远程仓储的使用操作.要参与任何一个 Git 项目的协作,必须要了解该如何管理远程仓库.远程仓库是指托管在网络上的项目仓库,可能会有好多个,其中有些你只能读,另外有 ...
- 如何使用adb命令查看android中的数据库
1,进入到控制台中,输入adb shell,进入到命令模式的环境中 2,输入:cd /data/data/ 3, 选择你所在的数据库文件,比如我的com.android.homework, 输入命令: ...
- Centos 6.5LAMP服务器(Apache+PHP+MySQL)的搭建
1.首先看下你的防火墙是否处于开启状态,如果是开启状态,按照如下方法来配置你的防火墙(如果你在安装虚拟机时就没有开启过防火墙,那么这一步就省略了): 1.配置防火墙,开启80端口.3306端口 vi ...