• 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. OO学习最终总结

    0.前言:本次主要是针对第三阶段的三次作业以及整个课程学习的总结,这三次作业主要是针对对程序进行重构使得程序可以对功能进行扩展的训练以及对类的设计,所以在此阐述在练习过程中的问题以及感悟. 1.作业总 ...

  2. CentOS8.1中搭建Gitlab服务器

    依旧是写在前面的话♠:很多IT人从业N年也许都还没有亲自搭过一次Gitlab服务器,是不是?有木有?!通常都是背着自己的笔记电脑到一家公司入职,或入职后领到公司分配的电脑,然后分配了Git账号,拿了将 ...

  3. 8000字长文让你彻底了解 Java 8 的 Lambda、函数式接口、Stream 用法和原理

    我是风筝,公众号「古时的风筝」.一个兼具深度与广度的程序员鼓励师,一个本打算写诗却写起了代码的田园码农! 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在 ...

  4. 一台电脑如何使用多个git账号?

    git相信大家都在用,一般公司有一个账号,放公司自己架的服务器中,员工自己还有一个github或者gitee的账号,存放自己的一些私有代码.本篇文章总结一下,本人在公司开发机上,使用多个git账号的干 ...

  5. 一文读懂Redis的四种模式,单机、主从、哨兵、集群

    少点代码,多点头发 本文已经被GitHub收录,欢迎大家踊跃star 和 issues. https://github.com/midou-tech/articles 入职第一周,我被坑了 最近刚入职 ...

  6. 基于docker-compose部署jumpserver

    基于docker-compose部署jumpserver 组件说明 Jumpserver 为管理后台, 管理员可以通过 Web 页面进行资产管理.用户管理.资产授权等操作, 用户可以通过 Web 页面 ...

  7. 详说tcp粘包和半包

    tcp服务端和客户端建立连接后会长时间维持这个连接,用于互相传递数据,tcp是以流的方式传输数据的,就像一个水管里的水一样,从一头不断的流向另一头. 理想情况下,发送的数据包都是独立的, 现实要复杂一 ...

  8. MFC基于CAsyncSocket套接字客户端代码示范

    MFC基于CAsyncSocket套接字客户端代码示范 https://blog.csdn.net/txwtech/article/details/93016190

  9. 对 JsonConvert 的认识太肤浅了,终于还是遇到了问题

    一:背景 1. 讲故事 在开始本文之前,真的好想做个问卷调查,到底有多少人和我一样,对 JsonConvert 的认识只局限在 SerializeObject 和 DeserializeObject ...

  10. 002.OpenShift安装与部署

    一 前置条件说明 1.1 安装准备概述 Red Hat OpenShift容器平台是由Red Hat作为RPM包和容器映像两种类型存在.RPM包使用订阅管理器从标准Red Hat存储库(即Yum存储库 ...