zoukankan      html  css  js  c++  java
  • java多线程-synchronized

    大纲:

    1. 线程安全问题
    2. synchronized代码块
    3. 原理简述

    一、线程安全问题

    多线程操作各自线程创建的资源的时候,不存在线程安全问题。但多线程操作同一个资源的时候就会出现线程安全问题。下例为两个线程操作同一个name资源时发生的问题。

    class TestSyn {
    
        public static void main(String[] args) throws Exception {
            Resource resource = new Resource();
            new Thread() {
                @Override
                public void run() {
                    while (true) {
                        r.sayb();
                    }
                }
            }.start();
            new Thread() {
                @Override
                public void run() {
                    while (true) {
                        r.sayq();
                    }
                }
            }.start();
        }
    }
    
    class Resource {
        private String name;
    
        public void sayb() {
            name = "bbb";
            System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
        }
    
        public void sayq() {
            name = "qqqqqq";
            System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
        }
    }
    /**
     *
     Thread-1qqqqqq
     Thread-1qqqqqq
     Thread-0qqqqqq //其中一段错误信息,Thread-0线程也打印了qqqqqq
     Thread-0bbb
     Thread-0bbb
     */

    问题出现过程:

    1. Thread-0获取执行权执行name="bbb"。
    2. Thread-1获得执行权执行name="qqqqqq"。
    3. Thread-0重新获得执行权执行打印指令,这时Thread-0就打印出了qqqqqq。

    二、synchronized代码块

    如果name赋值,打印name是一个原子操作就可以避免线程安全问题。

    java中synchronized可以标记一段代码,达到原子操作的效果。

    1. 当一个线程执行标记有synchronized代码时将获得该对象的锁,然后开始执行synchronized标记的代码。
    2. 每一个对象只有一个锁,因此其他线程无法获得该对象锁。
    3. 其他线程如果这时候也执行到了标记有synchronized的代码将阻塞,直到获得对象锁的线程执行完synchronized标记的代码。
    4. 然后持有锁的线程释放锁。
    5. 其他线程开始争夺锁,回到第1步。

     synchronized标记代码有两种方式:

    //synchronized代码块
    class Resource {
        private String name;
    
        public void sayb() {
            synchronized (this){
                name = "bbb";
                System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
            }
            //...其他代码
        }
    
        public void sayq() {
            synchronized (this){
                name = "qqqqqq";
                System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
            }
            //...其他代码
        }
    }
    //synchronized方法
    class Resource {
        private String name;
    
        public synchronized void sayb() {
            name = "bbb";
            System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
            //...其他代码
        }
    
        public synchronized void sayq() {
            name = "qqqqqq";
            System.out.println(Thread.currentThread().getName() + name);//Thread-0打印bbb
            //...其他代码
        }
    }

    上例中两个线程执行的是同一个对象的方法,因此他们抢夺同一个锁,一个线程执行的时候,另一个线程阻塞。

    两种方法有些不同点:

    1. synchronized方法标记在非static方法上,线程获得的锁为this,例子中为resource对象。若标记在static方法上则线程获得的锁为Resource.class对象。
    2. synchronized标记在代码块上,可以由用户自己指定,而且代码块的范围也可以自己指定。因此synchronized代码块比synchronized方法更加灵活。

     三、原理简述

    synchronized并非一开始就加上悲观锁,有一个锁升级的过程。无锁-->偏向锁-->轻量级锁-->重量级锁,锁可以升级不能降级,但可以撤销。

    锁对象头中有mark word,用于存放锁相关信息。

    无锁:锁对象头中mark word锁标志位初始为无锁状态。

    偏向锁::当有线程进入同步代码块,每一个线程栈帧中记录lock record,并将栈帧中obj指向锁对象,mark word存储栈帧地址(重入则地址相同),其他线程进入同步代码块,首先检查是否偏向锁,再检查获取锁线程中的obj是否指向锁对象,指向锁对象证明持有锁,存在竞争,锁升级。

    轻量级锁:mark word中记录的线程离开同步代码块会将栈帧中obj置null表示释放锁,期间其他线程自旋等待获取锁,竞争少 ,自旋次数少的情况下,轻量级锁性能比较好,因为用户态切换内核态是比较消耗性能的。当自旋超过一定次数或竞争线程超过一定数量时,锁升级。

    重量级锁:mark word中存放monitor对象地址,monitor主要存放持有锁的线程id,重入次数,wait线程链表,synchronized链表(没抢到锁的线程)等。没有获取到锁的线程进入blocked状态和被wait被唤醒的线程会被放到synchronized链表队尾,monitor释放锁,synchronized链表头被唤醒会和新来线程进行锁争抢,因此synchronized为非公平锁。

  • 相关阅读:
    Python基础03 序列
    Python基础04 运算
    Python基础01 Hello World!
    Python基础02 基本数据类型
    wpf 中AxShockwaveFlash重写以及屏蔽鼠标右键
    正则表达式判断金额
    解决jquery操作checkbox全选全不选无法勾选问题
    关于asp.net网址出现乱码问题的解决方法
    c#中用DirectShow实现媒体播放器的核心(1) DirectShow简介
    “XXX.Index”不扩展类“System.Web.UI.Page”,因此此处不允许的问题
  • 原文地址:https://www.cnblogs.com/liuboyuan/p/10083807.html
Copyright © 2011-2022 走看看