从此不怕Synchronized锁
- Synchronized的使用
Synchronized是通过监视器保证线程同步从而保证线程安全。但是Synchronized锁可以锁对象和锁类,并会产生不同的效果,通过下面的案例彻底理解Synchronized锁的使用方式。
即:
对于普通的同步方法,锁是当前实例对象
对于静态同步方法,锁是该类
对于同步方法块,锁是Synchronized括号里面配置的对象。
下面通过代码具体分析几种情况。要想了解并发情况,首先我们必须知道,类信息、实例对象分别存放在什么位置。类的信息,包括静态变量都是存放在方法区中;而实例对象,包括类的成员变量,是存放在堆中。
1. 成员变量+普通同步方法+锁
public class SynDemo implements Runnable {
private int sum = 0;
@Override
public void run() {
add();
}
public synchronized void add() {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync01);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sync01.sum);
System.out.println(new SynDemo().sum);
}
}
result:
2000
0
public class SynDemo implements Runnable {
private int sum = 0;
@Override
public void run() {
add();
}
public synchronized void add() {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
SynDemo sync02 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync02);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sync01.sum);
System.out.println(sync02.sum);
}
}
result:
1000
1000
0
分析:
理解了这两个demo再去理解同步代码块下的多线程安全问题,将会达到事半功倍的效果。上面两个demo主要是想表达,成员变量和类的实例化对象一样,都是在堆中创建,每次new对象,都会在堆中产生一个新的对象。所以第一个demo中,当在线程同步的情况下,两个线程去操作同一个对象,最后的结果是2000;而第二个demo中,两个线程去操作两个实例化对象,所以每个对象的成员变量sum为1000。因此我们也可以发现,其实在第一个demo中才会有线程安全问题,在第二个demo中是不存在线程安全问题的,有疑问可以去掉锁验证一下。通过这个例子也可以去理解为什么sping中多例是线程安全的。
2. 成员变量+同步代码块+对象锁
public class SynDemo implements Runnable {
private int sum = 0;
@Override
public void run() {
add();
}
private void add() {
synchronized (this) {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync01);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sync01.sum);
System.out.println(new SynDemo().sum);
}
}
result:
2000
0
public class SynDemo implements Runnable {
private int sum = 0;
@Override
public void run() {
add();
}
private void add() {
synchronized (this) {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
SynDemo sync02 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync02);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sync01.sum);
System.out.println(sync02.sum);
System.out.println(new SynDemo().sum);
}
}
result:
1000
1000
0
分析:
同案例1一样,Demo1为两个线程执行一个实例化对象,但是加了Synchronized对象锁,因此实现了同步,保证线程安全。Demo2为两个线程执行两个实例化对象,各自利用各自的成员变量sum,因此不会产生并发安全问题。
3. 成员变量+同步代码块+类锁
public class SynDemo implements Runnable {
private int sum = 0;
@Override
public void run() {
add();
}
private void add() {
synchronized (SynDemo.class) {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync01);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sync01.sum);
System.out.println(new SynDemo().sum);
}
}
result:
2000
0
public class SynDemo implements Runnable {
private int sum = 0;
@Override
public void run() {
add();
}
private void add() {
synchronized (SynDemo.class) {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
SynDemo sync02 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync02);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sync01.sum);
System.out.println(sync02.sum);
System.out.println(new SynDemo().sum);
}
}
result:
1000
1000
0
分析:
Demo1为两个线程执行一个实例化对象,会产生并发安全问题,但是加了同步类锁(可以理解为锁的级别比对象锁更高),当然也可以实现并发同步,保证线程安全。而Demo2同样实例化两个对象,各自操作各自的成员变量sum,也不会产生线程安全问题。
4. 静态变量+普通方法+锁
public class SynDemo implements Runnable {
private static int sum = 0;
@Override
public void run() {
add();
}
private synchronized void add() {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync01);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sum);
}
}
result:
2000
public class SynDemo implements Runnable {
private static int sum = 0;
@Override
public void run() {
add();
}
private synchronized void add() {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
SynDemo sync02 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync02);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sum);
}
}
输出结果不确定(存在线程安全问题)
分析:
从案例4我们要注意,由成员变量换成静态变量,而上面已经讲过,静态变量存放在方法区中,所有实例化对象共享一份。再看Demo1,两个线程执行同一个实例化对象,然后添加的是对象锁,因此该对象锁能锁住该实例化对象,实现同步,保证线程安全。
Demo2是两个线程执行两个实例化对象,添加的是对象锁,相当于各自的对象锁锁住各自的对象,而静态变量是类变量,存放在方法区中而不是堆中,此情况对象锁并不能保证线程同步,因此会产生线程安全问题。
5. 静态变量+静态方法+锁
public class SynDemo implements Runnable {
private static int sum = 0;
@Override
public void run() {
add();
}
static synchronized void add() {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync01);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sum);
}
}
result:
2000
public class SynDemo implements Runnable {
private static int sum = 0;
@Override
public void run() {
add();
}
static synchronized void add() {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
SynDemo sync02 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync02);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sum);
}
}
result:
2000
分析:
该案例相比案例4,锁由对象锁换成类锁,对于Demo1,两个线程操作一个对象,毫无疑问会使其同步。而Demo2,两个线程执行两个实例化对象,由于使用的是类锁,也会使线程同步,保证线程安全。
6. 静态变量+同步代码块+对象锁
public class SynDemo implements Runnable {
private static int sum = 0;
@Override
public void run() {
add();
}
private void add() {
synchronized (this) {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync01);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sum);
}
}
result:
2000
public class SynDemo implements Runnable {
private static int sum = 0;
@Override
public void run() {
add();
}
private void add() {
synchronized (this) {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
SynDemo sync02 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync02);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sum);
}
}
输出结果不确定(存在线程安全问题)
分析:该案例和案例4一样,添加对象锁,只能保证同一对象的并发同步,不能保证不同对象同步。
7. 静态变量+同步代码块+类锁
public class SynDemo implements Runnable {
private static int sum = 0;
@Override
public void run() {
add();
}
private void add() {
synchronized (SynDemo.class) {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync01);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sum);
}
}
result:
2000
public class SynDemo implements Runnable {
private static int sum = 0;
@Override
public void run() {
add();
}
private void add() {
synchronized (SynDemo.class) {
for (int i = 0; i < 1000; i++) {
sum++;
}
}
}
public static void main(String[] args) throws InterruptedException {
SynDemo sync01 = new SynDemo();
SynDemo sync02 = new SynDemo();
Thread thread1 = new Thread(sync01);
Thread thread2 = new Thread(sync02);
thread1.start();
thread2.start();
thread1.join(); //等待线程执行完
thread2.join(); //等待线程执行完
System.out.println(sum);
}
}
result:
2000
分析:
该案例同案例5一样,添加类锁,无论是多个线程操作一个实例化对象还是多个实例化对象,都能保证线程安全。
总结:
对象锁只能保证各自实例化对象并发的线程安全问题。类锁可以保证多个实例化多谢的安全问题。
从此不怕Synchronized锁的更多相关文章
- synchronized锁重入
package synLockIn_1; /* synchronized锁重入,当一个线程得到一个对象锁且还未释放锁时,再次请求此对象锁时可以再次得到该对象的锁 * 此例中线程1进入Service类的 ...
- Java多线程4:synchronized锁机制
脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过 ...
- synchronized锁自旋
http://www.jianshu.com/p/5dbb07c8d5d5 原理 通常说的synchronized在方法或块上加锁,这里的锁就是对象锁(当然也可以在类上面),或者叫重量锁,在JVM中又 ...
- 记一次synchronized锁字符串引发的坑兼再谈Java字符串
问题描述 业务有一个需求,我把问题描述一下: 通过代理IP访问国外某网站N,每个IP对应一个固定的网站N的COOKIE,COOKIE有失效时间.并发下,取IP是有一定策略的,取到IP之后拿IP对应的C ...
- 记一次 synchronized 锁字符串引发的坑兼再谈 Java 字符串
业务有一个需求,我把问题描述一下: 通过代理IP访问国外某网站N,每个IP对应一个固定的网站N的COOKIE,COOKIE有失效时间. 并发下,取IP是有一定策略的,取到IP之后拿IP对应的COOKI ...
- Synchronized锁在Spring事务管理下,为啥还线程不安全?
前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 大年初二,朋友问了我一个技术的问题(朋友实在是好学, ...
- Synchronized锁性能优化偏向锁轻量级锁升级 多线程中篇(五)
不止一次的提到过,synchronized是Java内置的机制,是JVM层面的,而Lock则是接口,是JDK层面的 尽管最初synchronized的性能效率比较差,但是随着版本的升级,synchro ...
- 多线程之Synchronized锁的基本介绍
基本介绍 synchronized是Java实现同步的一种机制,它属于Java中关键字,是一种jvm级别的锁.synchronized锁的创建和释放是此关键字控制的代码的开始和结束位置,锁是有jvm控 ...
- Java多线程6:Synchronized锁代码块(this和任意对象)
一.Synchronized(this)锁代码块 用关键字synchronized修饰方法在有些情况下是有弊端的,若是执行该方法所需的时间比较长,线程1执行该方法的时候,线程2就必须等待.这种情况下就 ...
随机推荐
- 【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则
写在前面 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository.@Service.@Controller.@Compon ...
- iOS-自定义Model转场动画-仿酷我音乐播放器效果
周末,闲来无事,仿写了酷我音乐播放器效果: 效果图如下: 实现思路: 1.实现手势处理视图旋转 2.自定义Model动画: 1.手势是利用了一个UIPanGestureRecognizer手势: 注意 ...
- 别让HR再质问我:我费劲招的人,你用缓存问废了,不能简单点?
概念 缓存穿透 在高并发下,查询一个不存在的值时,缓存不会被命中,导致大量请求直接落到数据库上,如活动系统里面查询一个不存在的活动. 缓存击穿 在高并发下,对一个特定的值进行查询,但是这个时候缓存正好 ...
- @bzoj - 2658@ [Zjoi2012]小蓝的好友(mrx)
目录 @description@ @solution@ @accepted code@ @details@ @description@ 终于到达了这次选拔赛的最后一题,想必你已经厌倦了小蓝和小白的故事 ...
- [计网笔记] 传输层---UDP
- TensorFlow从0到1之XLA加速线性代数编译器(9)
加速线性代数器(Accelerated linear algebra,XLA)是线性代数领域的专用编译器.根据 https://www.tensorflow.org/performance/xla/, ...
- usb串口的作用以及JLINK
usb串口的作用 (1)可以当串口使用 (2)如果usb串口连接到STM32的串口1(stm32ISP下载只能是串口1),可以用串口下载程序 (3)因为要连接到usb,可以用来供电 JLINK JLI ...
- android中的逐帧动画
在android中实现动画最简单的一种方式就是使用逐帧动画(AnimationDrawable).逐帧动画的原理同最古老的动画机制是一样的,通过快速的播放一组变化微小的图片,在人眼的视差时间下,达到一 ...
- 红米手机 android4.4.4 root之路
第一步: 进入360root官网下载apk安装包: http://root.360.cn/index.html 说明:不是所有的机型都能root, 一般android5.0 以下的系统root的成功 ...
- el-checkbox实现全选与单选
实现目的:实现全选与多选,点击确定的时候获取每个值的id传给后台 1.HTML <el-checkbox v-model="checkAll" @change="h ...