从此不怕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就必须等待.这种情况下就 ...
随机推荐
- 【JAVA习题三十】求0—7所能组成的奇数个数
package erase; public class 求0到7所能组成的奇数个数 { public static void main(String[] args) { /* * 求0—7所能组成的奇 ...
- 从linux源码看epoll
从linux源码看epoll 前言 在linux的高性能网络编程中,绕不开的就是epoll.和select.poll等系统调用相比,epoll在需要监视大量文件描述符并且其中只有少数活跃的时候,表现出 ...
- monkey命令的基本使用
看到monkey,你想到了什么?今天给大家分享下monkey命令的基本使用 monkey测试是Android平台自动化测试的一种手段,通过monkey程序模拟用户触摸屏幕,滑动.按键操作等操作对设备上 ...
- javaweb之Servlet,http协议以及请求转发和重定向
本文是作者原创,版权归作者所有.若要转载,请注明出处. 一直用的框架开发,快连Servlet都忘了,此文旨在帮自己和大家回忆一下Servlet主要知识点.话不多说开始吧 用idea构建Servlet项 ...
- centos6 升级python2.6 到 python2.7
由于开发库依赖于python27,而自己安装的centos6.8自带的python是2.6.6. 因为centos的yum依赖于python26因此不打算覆盖26. 步骤如下: 1.官网下载源码压缩包 ...
- 匿名实现类&匿名对象
学习过程中经常搞不清匿名类&匿名对象怎么用,今天就把常用的方式总结一遍. 1.创建了非匿名实现类的非匿名对象 //定义USB接口 interface USB{ void inputInofo( ...
- Python format格式化函数
参考资料:https://www.runoob.com/python/att-string-format.html 在学习Python的时候碰到了一个很有趣的格式化输入的技巧,下面记录在此. Pyth ...
- 多语言工作者の十日冲刺<6/10>
这个作业属于哪个课程 软件工程 (福州大学至诚学院 - 计算机工程系) 这个作业要求在哪里 团队作业第五次--Alpha冲刺 这个作业的目标 团队进行Alpha冲刺--第六天(05.05) 作业正文 ...
- Nginx 如何自定义变量?
之前的两篇文章 Nginx 变量介绍以及利用 Nginx 变量做防盗链 讲的是 Nginx 有哪些变量以及一个常见的应用.那么如此灵活的 Nginx 怎么能不支持自定义变量呢,今天的文章就来说一下自定 ...
- 一起玩转微服务(10)——spring boot介绍
对于Spring,相信大家都非常熟悉,从出现开始,一直是企业级开发的主流.但是随着软件的发展和应用开发的不断演化,它的一些缺点也逐渐胡暴露了出来,下面,我们就一起看一下Spring的发展历程并且认识一 ...