B4. Concurrent JVM 锁机制(synchronized)

https://www.cnblogs.com/zlxyt/p/11050346.html

挺好的 感觉这个文章写的 不过想要提高 还是得自己写代码 不写代码 肯定不行. 

【概述】

  JVM 通过 synchronized 关键字提供锁,用于在线程同步中保证线程安全。

【synchronized 实现原理】

  synchronized 可以用于代码块或者方法中,产生同步代码区域,也叫互斥区。互斥区每次只能允许一个线程进入执行同步代码或重新进入执行剩余同步代码(参考线程进入等待状态后会唤醒,然后进入阻塞状态,重新获得锁的情况)。

  synchronized 通过与一个对象进行绑定,或者说对一个对象进行加锁,并产生一个监控对象(monitor object)。如下图所示:

  • 多线程并发执行同步代码,首先多个线程会进入集合(Entry Set),某个时间点只有一个线程可以获得(acquire) 监控对象的锁(monitor lock), 然后执行同步代码块。
  • 如果获得锁的线程执行同步代码完毕,则会释放锁并退出同步代码区域(release and exit)。
  • 若获得锁的线程在执行同步代码时调用被锁住对象的 wait 方法,该线程则会释放(release)锁,并进入集合(Wait Set)。锁被释放后,其他位于集合(Entry Set)的线程即可争夺监控对象的锁(monitor lock)。通过调用对象的 notify 或 notifyAll 方法可以唤醒集合(Wait Set)中的线程,不同的是 notify 会唤醒集合(Wait Set)中的其中一个不确定的线程,notifyAll 会唤醒集合(Wait Set)中的所有线程。
  • 集合(Wait Set)中的线程被唤醒后,会重新进入集合(Emtry Set)与该集合中的其他线程一起争夺监控对象的锁(monitor lock),获得锁后会继续执行 wait 方法后面的代码。

【synchronized 加锁对象】

  JVM 中使用 synchronized 进行加锁的对象有两种:分别为存储于 Java 堆的实例对象和存储于方法区的类信息对象。

  1). synchronized 修饰实例方法,是对该方法所属的具体实例对象加锁;

  2). synchronized 修饰静态方法,是对该方法所属的类信息对象加锁;

  3). synchronized 同步代码块中的参数为 Class 对象或静态对象,则对 Class 对应的类信息对象加锁;

  4). synchronized 同步代码块中的参数非 Class 对象或静态对象,而为其他实例对象,则对具体的实例对象加锁。

  对实例对象和类信息对象加锁的区别:

  • 对实例对象进行加锁,同一时刻只有一个线程获得锁,即只有一个线程获得该实例对象的使用权,其他线程无法使用该实例对象的同步代码区域。注意:对一个实例对象进行加锁,不会影响其他实例对象中同步代码区域的执行。
  • 对类信息对象进行加锁,同一时刻只有一个线程获得锁,即只有一个线程可以调用对应类的同步代码区域(包括同步静态方法、同步实例方法、同步代码块),其他线程无法调用对应类的同步代码区域。注意:对一个类信息对象进行加锁,会影响该类所有实例对象中的同步代码区域的执行。

【synchronized 代码实践】

  1). 多线程并发执行同一个实例对象的方法, 没有加锁的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
 * 多线程并发执行同一个实例对象的方法, 没有加锁的情况
 */
public class Main{
    public static void main(String[] args){
        Command c = new Command();
        int nThreads = 10;
        Thread[] arr = new Thread[nThreads];
        for(int n = 0; n < nThreads; n++){
            arr[n] = new Thread(c);
        }
        for(int n = 0; n < nThreads; n++){
            arr[n].start();
        }
    }
}
 
class Command implements Runnable{
     
    private int i = 0;
     
    @Override
    public void run() {
        add();
    }
     
    public void add(){
        i++;
        System.out.println(Thread.currentThread().getName() + ": " + i);
    }
}

  从下面打印结果可以看出:在没有加锁的情况下,多线程并发执行 i++,并没有按照顺序输出,并出现了线程安全的问题。

  

  2). 多线程并发,对实例方法进行加锁:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * 多线程并发,对实例方法进行加锁
 */
public class Main{
    public static void main(String[] args){
        Command c = new Command();
        int nThreads = 10;
        for(int n = 0; n < nThreads; n++){
            new Thread(c).start();
        }
    }
}
 
class Command implements Runnable{
     
    private int i = 0;
     
    @Override
    public void run() {
        add();
    }
     
    public synchronized void add(){
        i++;
        System.out.println(Thread.currentThread().getName() + ": " + i);
    }
}

  打印结果: 对实例方法进行加锁,即对实例对象进行加锁,多线程并发执行实例对象的方法时互斥执行,结果按顺序输出,且解决了线程安全的问题。

  

  3). 多线程并发执行实例对象的方法时,同步代码块锁住实例对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * 多线程并发执行实例对象的方法时,同步代码块锁住实例对象
 */
public class Main{
    public static void main(String[] args){
        Command c = new Command();
        int nThreads = 10;
        for(int n = 0; n < nThreads; n++){
            new Thread(c).start();
        }
    }
}
 
class Command implements Runnable{
     
    private int i = 0;
     
    @Override
    public void run() {
        add();
    }
     
    public void add(){
        synchronized(this){
            i++;
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

  打印结果:可以看出同步代码块锁住实例对象的效果和对实例方法的效果一样,多线程并发执行实例对象的方法时互斥执行,结果按顺序输出,且解决了线程安全的问题。实际上这两种方法都是对实例对象进行加锁,不同的是同步方法(被加锁后的方法)互斥的内容是整个方法体,代码块互斥的内容是整个代码块,相对而言后者影响较小。使用同步代码块替代同步方法,缩小了锁粒度,也是锁优化的一种方式。

  

  4). 多线程并发,对实例对象加锁,其他线程是否可以调用该实例对象的非同步代码区域?是否可以调用该实例对象的同步代码区域?

   4.1). 设计了两个方法,一个生产方法(produce),一个消费方法(consume),对生成方法进行加锁,锁住实例对象,对消费方法不加锁,看下执行情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Main{
    public static void main(String[] args){
        Command c = new Command();
        int nThreads = 5;
        for(int n = 0; n < nThreads; n++){
            new Thread(c).start();
        }
    }
}
 
class Command implements Runnable{
     
    @Override
    public void run() {
        produce();
        consume();
    }
     
    public synchronized void produce(){
        System.out.println(Thread.currentThread().getName() + " produce start");
        System.out.println(Thread.currentThread().getName() + " produce finish");
    }
     
    public void consume(){
        System.out.println(Thread.currentThread().getName() + " consume start");
        System.out.println(Thread.currentThread().getName() + " consume finish");
    }
}

  打印结果:可以看出当生产方法执行的时候,消费方法也在执行。由此可见,对实例对象加锁对于调用该实例对象的非同步代码区域,是没有影响的。

  

  4.2). 设计了两个方法,一个生产方法(produce),一个消费方法(consume),对实例对象的生成方法和消费方法同时加锁,看下执行情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class Main{
    public static void main(String[] args){
        Command c = new Command();
        int nThreads = 5;
        for(int n = 0; n < nThreads; n++){
            new Thread(c).start();
        }
    }
}
 
class Command implements Runnable{
     
    @Override
    public void run() {
        produce();
        consume();
    }
     
    public synchronized void produce(){
        System.out.println(Thread.currentThread().getName() + " produce start");
        System.out.println(Thread.currentThread().getName() + " produce finish");
    }
     
    public synchronized void consume(){
        System.out.println(Thread.currentThread().getName() + " consume start");
        System.out.println(Thread.currentThread().getName() + " consume finish");
    }
}

  打印结果:可以看出生产方法和消费方法互斥执行。由此可见,对实例对象加锁对于调用该实例对象的同步代码区域,是影响的。不同的线程执行同一实例对象的不同的同步方法,需要竞争同一把锁,互斥执行。

  

  5). 多线程并发执行静态同步方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/**
 * 多线程并发执行静态同步方法
 */
public class Main{
    public static void main(String[] args){
        Command c = new Command();
        int nThreads = 10;
        for(int n = 0; n < nThreads; n++){
            new Thread(c).start();
        }
    }
}
  
class Command implements Runnable{
      
    private int i = 0;
      
    @Override
    public void run() {
        add();
    }
      
    public synchronized void add(){
        i++;
        System.out.println(Thread.currentThread().getName() + ": " + i);
    }
}

  打印结果:

  

  6). 多线程并发,同步代码块锁住类信息对象 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
 * 多线程并发执行静态同步方法
 */
public class Main{
    public static void main(String[] args){
        Command c = new Command();
        int nThreads = 10;
        for(int n = 0; n < nThreads; n++){
            new Thread(c).start();
        }
    }
}
  
class Command implements Runnable{
      
    private int i = 0;
      
    @Override
    public void run() {
        add();
    }
      
    public void add(){
        synchronized(Command.class){
            i++;
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
    }
}

  打印结果:

  

  7). 多线程并发,对类信息对象加锁,是否可以调用不同实例对象的同步代码区域?

  7.1)  多线程并发执行不同实例对象的同步方法,不对类信息对象加锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Main{
    public static void main(String[] args){
        int nThreads = 5;
        for(int n = 0; n < nThreads; n++){
            new Thread(new Command()).start();
        }
    }
}
  
class Command implements Runnable{
     
    @Override
    public void run() {
        produce();
        consume();
    }
     
    public synchronized void produce(){
        System.out.println(Thread.currentThread().getName() + " produce start");
        System.out.println(Thread.currentThread().getName() + " produce finish");
    }
     
    public synchronized void consume(){
        System.out.println(Thread.currentThread().getName() + " consume start");
        System.out.println(Thread.currentThread().getName() + " consume finish");
    }
}

  打印结果:可以看出不同实例对象并发执行同步代码块,不会互斥执行。

  

  7.2)  多线程并发执行不同实例对象的同步方法,对类信息对象加锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Main{
    public static void main(String[] args){
        int nThreads = 5;
        for(int n = 0; n < nThreads; n++){
            new Thread(new Command()).start();
        }
    }
}
  
class Command implements Runnable{
     
    @Override
    public void run() {
        produce();
        consume();
    }
     
    public void produce(){
        synchronized(Command.class){
            System.out.println(Thread.currentThread().getName() + " produce start");
            System.out.println(Thread.currentThread().getName() + " produce finish");
        }
    }
     
    public synchronized void consume(){
        System.out.println(Thread.currentThread().getName() + " consume start");
        System.out.println(Thread.currentThread().getName() + " consume finish");
    }
}

  打印结果:在生产方法(produce)对类信息对象进行加锁,可以看到生成方法(produce)执行时与消费方法(consume)是互斥的。

  

  

[转帖]B4. Concurrent JVM 锁机制(synchronized)的更多相关文章

  1. B4. Concurrent JVM 锁机制(synchronized)

    [概述] JVM 通过 synchronized 关键字提供锁,用于在线程同步中保证线程安全. [synchronized 实现原理] synchronized 可以用于代码块或者方法中,产生同步代码 ...

  2. [转载]深入JVM锁机制-synchronized

    转自:http://blog.csdn.net/chen77716/article/details/6618779,并加上少量自己的理解 目前在Java中存在两种锁机制:synchronized和Lo ...

  3. 转:synchronized和LOCK的实现原理---深入JVM锁机制

    JVM底层又是如何实现synchronized的? 目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug ...

  4. java多线程之:深入JVM锁机制2-Lock (转载)

    前文(深入JVM锁机制-synchronized)分析了JVM中的synchronized实现,本文继续分析JVM中的另一种锁Lock的实现.与synchronized不同的是,Lock完全用Java ...

  5. 深入JVM锁机制2-Lock

    前文(深入JVM锁机制-synchronized)分析了JVM中的synchronized实现,本文继续分析JVM中的另一种锁Lock的实现.与synchronized不同的是,Lock完全用Java ...

  6. 【转载】Java中的锁机制 synchronized & 偏向锁 & 轻量级锁 & 重量级锁 & 各自优缺点及场景 & AtomicReference

    参考文章: http://blog.csdn.net/chen77716/article/details/6618779 目前在Java中存在两种锁机制:synchronized和Lock,Lock接 ...

  7. java 锁机制(synchronized 与 Lock)

    在java中,解决同步问题,很多时候都会使用到synchronized和Lock,这两者都是在多线程并发时候常使用的锁机制. synchronized是java中的一个关键字,也就是说是java内置的 ...

  8. 深入JVM锁机制1-synchronized

    目前在Java中存在两种锁机制:synchronized和Lock,Lock接口及其实现类是JDK5增加的内容,其作者是大名鼎鼎的并发专家Doug Lea.本文并不比较synchronized与Loc ...

  9. Java 锁机制 synchronized

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/75126630 本文出自[赵彦军的博客] 1.前言 在多线程并发编程中Synchro ...

随机推荐

  1. 开发工具--搭建python环境

    工具|搭建python环境 实现python2版本与python3版本的环境搭建. 正文 1.Python下载 官网: www.python.org 下载: ( 64位3.5.2Windows x86 ...

  2. 如何才能通俗易懂的解释js里面的‘闭包’?

    1. "闭包就是跨作用域访问变量." [示例一] var name = 'wangxi' function user () { // var name = 'wangxi' fun ...

  3. scrapy-redis 0.6.8 配置信息

    很多博客的db参数配置都不能用,所以记录一下该版本可用的配置 #启用Redis调度存储请求队列 SCHEDULER = "scrapy_redis.scheduler.Scheduler&q ...

  4. python使用sched模块执行周期性任务和定时任务

    执行周期性任务 sched模块是一个通用的事件调度程序,可以对任务进行延迟调度,基于此,可以用它来实现周期性任务. # coding:utf8 import time import sched # 初 ...

  5. idea 2019 集成activiti, idea activiti 新建bpmn文件, 解决idea activiti中文乱码

    idea 在线安装activiti插件 1. File-->Settings 2. 点击Plugins, 右侧界面点击Marketplace后在搜索框搜索 actiBPM 注: 网络原因没有加载 ...

  6. 网络编程socket理论一

    网络通信理论 首先说下网络之间的通信流程看图 Socket 层是位于运输层和应用层之间的,封装好的,方便使用 Socket 是什么和工作流程 Socket是应用层与TCP/IP协议族通信的中间软件抽象 ...

  7. 大众点评评论数据抓取 反爬虫措施有css文字映射和字体库反爬虫

    大众点评评论数据抓取  反爬虫措施有css文字映射和字体库反爬虫 大众点评的反爬虫手段有那些: 封ip,封账号,字体库反爬虫,css文字映射,图形滑动验证码 这个图片是滑动验证码,访问频率高的话,会出 ...

  8. xdvipdfmx:fatal: Unable to open "xxx.pdf". Output file removed. fwrite: Broken pipe xelatex.exe

    解决方法: 关闭外部PDF阅读器,重新编译运行下就好了. 参考:https://github.com/James-Yu/Atom-LaTeX/issues/109

  9. 数据库中的gt,ge,lt,le的区别

    eq相等   ne.neq不相等,   gt大于, lt小于 gte.ge大于等于   lte.le 小于等于   not非   mod求模   is [not] div by是否能被某数整除   i ...

  10. NOIP 2006 明明的随机数

    洛谷 P1059 明明的随机数 洛谷传送门 JDOJ 1423: [NOIP2006]明明的随机数 T1 JDOJ传送门 Description 明明想在学校中请一些同学一起做一项问卷调查,为了实验的 ...