• 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. Spring Cloud 系列之 Apollo 配置中心(四)

    本篇文章为系列文章,未读前几集的同学请猛戳这里: Spring Cloud 系列之 Apollo 配置中心(一) Spring Cloud 系列之 Apollo 配置中心(二) Spring Clou ...

  2. 使用请求头认证来测试需要授权的 API 接口

    使用请求头认证来测试需要授权的 API 接口 Intro 有一些需要认证授权的接口在写测试用例的时候一般会先获取一个 token,然后再去调用接口,其实这样做的话很不灵活,一方面是存在着一定的安全性问 ...

  3. MongoDB知识点总结

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

  4. PCB制图--VCC、VDD、VEE、VSS、VDDA、VSSA

    VDDA为所有的模拟电路部分供电,包括: ADC模块,复位电路,PVD(可编程电压监测器),PLL,上电复位(POR)和掉电复位(PDR)模块,控制VBAT切换的开关等.即使不 使用ADC功能,也需要 ...

  5. ubuntu qwt6.1.0安装

    1.ubuntu-12.04 qt-5.1.1 2.sudo apt-get install libgl1-mesa-dev libglu1-mesa-dev 3.qmake 4.make 5.sud ...

  6. 双剑合璧 Nacos 结合 Sentinel 实现流量安全控制

    Alibaba Sentinel 是一款高性能且轻量级的流量控制.熔断降级解决方案.是面向分布式服务架构的高可用流量控制组件. Sentinel 官网:https://sentinelguard.io ...

  7. oracle 锁表解决方式

    /*查看被锁住的存储过程*/ SELECT * FROM V$DB_OBJECT_CACHE WHERE OWNER = 'APPADMIN' AND LOCKS != '0'; SELECT * F ...

  8. Github上可以涨薪30k的Java教程和实战项目终于可以免费下载了

    写在前面 大家都知道 Github 是一个程序员福地,这里有各种厉害的开源框架.软件或者教程.这些东西对于我们学习和进步有着莫大的进步,所以我有了这个将 Github 上非常棒的 Java 开源项目整 ...

  9. php5.5下安装pdflib的步骤

    php5.5下安装pdflib的步骤 1. 下载pdflib 下载地址为:http://www.pdflib.com/download/pdflib-family/pdflib/ 然后选择对应的版本, ...

  10. LevelDB/Rocksdb 特性分析

    LevelDb是Google开源的嵌入式持久化KV 单机存储引擎.采用LSM(Log Structured Merge)tree的形式组织持久化存储的文件sstable.LSM会造成写放大.读放大的问 ...