• 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锁的更多相关文章

  1. synchronized锁重入

    package synLockIn_1; /* synchronized锁重入,当一个线程得到一个对象锁且还未释放锁时,再次请求此对象锁时可以再次得到该对象的锁 * 此例中线程1进入Service类的 ...

  2. Java多线程4:synchronized锁机制

    脏读 一个常见的概念.在多线程中,难免会出现在多个线程中对同一个对象的实例变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实是被更改过 ...

  3. synchronized锁自旋

    http://www.jianshu.com/p/5dbb07c8d5d5 原理 通常说的synchronized在方法或块上加锁,这里的锁就是对象锁(当然也可以在类上面),或者叫重量锁,在JVM中又 ...

  4. 记一次synchronized锁字符串引发的坑兼再谈Java字符串

    问题描述 业务有一个需求,我把问题描述一下: 通过代理IP访问国外某网站N,每个IP对应一个固定的网站N的COOKIE,COOKIE有失效时间.并发下,取IP是有一定策略的,取到IP之后拿IP对应的C ...

  5. 记一次 synchronized 锁字符串引发的坑兼再谈 Java 字符串

    业务有一个需求,我把问题描述一下: 通过代理IP访问国外某网站N,每个IP对应一个固定的网站N的COOKIE,COOKIE有失效时间. 并发下,取IP是有一定策略的,取到IP之后拿IP对应的COOKI ...

  6. Synchronized锁在Spring事务管理下,为啥还线程不安全?

    前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 大年初二,朋友问了我一个技术的问题(朋友实在是好学, ...

  7. Synchronized锁性能优化偏向锁轻量级锁升级 多线程中篇(五)

    不止一次的提到过,synchronized是Java内置的机制,是JVM层面的,而Lock则是接口,是JDK层面的 尽管最初synchronized的性能效率比较差,但是随着版本的升级,synchro ...

  8. 多线程之Synchronized锁的基本介绍

    基本介绍 synchronized是Java实现同步的一种机制,它属于Java中关键字,是一种jvm级别的锁.synchronized锁的创建和释放是此关键字控制的代码的开始和结束位置,锁是有jvm控 ...

  9. Java多线程6:Synchronized锁代码块(this和任意对象)

    一.Synchronized(this)锁代码块 用关键字synchronized修饰方法在有些情况下是有弊端的,若是执行该方法所需的时间比较长,线程1执行该方法的时候,线程2就必须等待.这种情况下就 ...

随机推荐

  1. 视频处理之OSD

    欲观原文,请君移步 OSD简介 OSD,on-screen display的简称,即屏幕菜单式调节方式.一般我们按一下Menu键后屏幕弹出的显示器各项调节项目信息的矩形菜单,比如调亮度,色调,饱和度等 ...

  2. leetcode之两数相加解题思路

    问题描述 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,数组中同一个元素不能使 ...

  3. MongoDB知识点总结

    一:MongoDB 概述    一.NoSQL 简介 1. 概念:NoSQL(Not Only SQL的缩写),指的是非关系型数据库,是对不同于传统的关系型数据库的数据库管理系统的统称.用于超大规模数 ...

  4. 第一个SpringMVC程序 (注解版)

    1.新建一个web项目 2.导入相关jar包 3.编写web.xml , 注册DispatcherServlet <?xml version="1.0" encoding=& ...

  5. 最全的DOM事件笔记

    1. DOM事件模型 DOM是微软和网景发生"浏览器大战"时期留下的产物,后来被"W3C"进行标准化,标准化一代代升级与改进,目前已经推行至第四代,即 leve ...

  6. 我深爱的Java,对不起,我出轨了!!!呸!渣男!

    我对Java情有独钟 大学三年来,我主学的编程语言一直是Java,为了学好它,我付出了很多心血.现在回想,确实是Java改变了我,造就了我. 因为Java,我自愿在学校组织学弟学妹,给他们讲解Java ...

  7. Android SDK 安装与配置

    1.下载sdk包 链接:https://pan.baidu.com/s/1Og8F02YBJn59LPWsJwkjUA 提取码:byu1 复制这段内容后打开百度网盘手机App,操作更方便哦 2.解压 ...

  8. wget介绍和命令总结

    参考资料: https://www.cnblogs.com/ftl1012/p/9265699.html https://www.cnblogs.com/lsdb/p/7171779.html cur ...

  9. 汇编字符串末尾以00H或 0AH和00H结尾

    例如:db 'hello',0 用 C 语言百定义字符串时,编译软件会自动在字符串的末尾,加上一个零('\0').作为度字符串结束的标记. 用汇编的 DB 伪指令定义字符串,编译软件没有自动加上零的功 ...

  10. npm设置为淘宝镜像地址

    1. npm设置为淘宝镜像 $npm config set registry https://registry.npm.taobao.org 2. 检查一下 $npm config get regis ...