zoukankan      html  css  js  c++  java
  • Synchronized的作用

    Synchronized

    官方解释:

      同步方法支持一种简单的策略来防止线程干扰和内存一致性错误:如果一个对象对多个线程可见,则对该对象变量的所有读取或写入都是通过同步方法完成的。

    一句话总结出Synchronized的作用:

      能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果

    Synchronized的地位:

      synchronized是Java的关键字,被Java语言原生支持

      是最基本的互斥同步手段

      必学

    synchronized的两个用法

    对象锁:

      包含方法锁(默认锁对象为this当前实力对象),同步代码块锁(自己制定锁对象)

    类锁:

      指sychronized修饰静态的方法或指锁为Class对象

    第一个用法:对象锁

      代码块形式:手动指定锁对象

      方法锁形式:synchronized修饰普通方法,锁默认对象为this

    第二个用法:类锁

      概念:java类可能有有很多个对象,但是只有一个class对象

      本质:所以所谓的类锁,不过是Class对象的锁而已

      用法和效果:类锁只能在同一时刻被一个对象拥有

      形式1:synchronized加载static方法上

      形式2:synchronized(*.class)代码块

    多线程访问同步方法的7种情况

    1.两个线程同时访问一个对象的同步方法

      串行

    2.两个线程访问的是两个对象的同步方法

      锁对象不同,互不干扰,并行

    3.如果两个线程访问的是Synchronized的静态方法

      串行

    4.同时访问同步方法与非同步方法

      并行

    5.访问同一对象的不同的普通同步方法

      同一对象锁,串行

    6.同时访问静态synchronized和非静态synchronized方法

      锁不同,并行

    7.方法抛异常后,会释放锁吗

      如果一个线程在进入同步方法后抛出了异常,则另一个线程会立刻进入该同步方法

    7种情况的总结

    1.一把锁只能同时被一个线程获取,没有拿到锁的线程必须等待(对应1,5种情况)

    2.每个实例都对应有自己的一把锁,不同实例之间互不影响,例如,锁对象是*.class以及synchronized修饰的

    是static方法的时候,所有对象共用同一把锁(对应第2,3,4,6种情况)

    3.无论是方法正常执行完步或者方法抛出异常,都会释放锁(对应第7种情况)

    性质

    1.可重入

    什么是可重入(递归锁)-----例如:Synchronized,ReentranLock

      指的是同一线程的外层方法获得获得锁之后,内层方法可以直接在此获取该锁

        例如: 买完车,需要摇号上车牌(锁),这样才能开车(执行相应的方法).

        如果我有三辆车,但只有一个车牌,显然只能给一辆车上车牌----获取锁后只能执行一个方法

                                          ---------------为不可重入性

        反之:一个车牌,三辆车都可以开

                                     ---------------为可重入性

    好处:

      避免死锁,提高封装性

    粒度(范围):

      可重入粒度测试: 证明线程而非调用(用三种情况来说明和pthread的区别)

        情况一:证明同一方法是可重入的---递归调用本方法

    package cn.cg;
    /**
     * 可重入测试----使用递归方式   
     * 情况1:同一个类中,同一方法可重入
     */
    public class SynchronizedTest {
        int i = 0;
        //主线程可以重入以this为锁对象的method方法
        public static void main(String[] args) {
            SynchronizedTest s1 = new  SynchronizedTest();
            s1.method();
        }
        // 同步方法
        private synchronized void method() {
            if (i <=3) {
                i++;
                System.out.println(i);
                method();
            }
        }
    }

     测试通过.

        情况二:证明可重入不要求是同一个方法

    package cn.cg;
    /**
     * 可重入测试----   
     * 情况2:同一个类中,不同方法可重入
     */
    public class SynchronizedTest2 {
        //主线程可以重入以this为锁对象的method方法
        public static void main(String[] args) {
            SynchronizedTest2 s1 = new  SynchronizedTest2();
            s1.method1();
        }
        // 同步方法
        private synchronized void method1() {
            System.out.println("method1");
                method2();
            
        }
        private synchronized void method2() {
            System.out.println("method2");
        }
    }

     测试通过

        情况三:证明可重入不要求是同一个类中的

    public class Demo1 {
        public synchronized void method(){
            System.out.println("我是Demo1");
        }
    
    }
    class Demo1Zi extends  Demo1{
        public synchronized void method(){
            System.out.println("我是Demo1儿子");
            super.method();
        }
    
        public static void main(String[] args) {
            Demo1Zi zi = new Demo1Zi();
            zi.method();
        }
    }

    测试通过

    2.不可中断

      一旦这个锁已经被别人获得了,如果我还想获取,我只能选择等待或者阻塞,直到别的线程释放这个锁.如果别人永远不释放锁,那么我只能永远地等下去

      相比之下,Lock类,可以拥有中断的能力,

        第一点:如果我觉得我等的时间太长了,有权中断现在已经获取到锁的线程的执行,

        第二点:如果我绝得我等待的时间太长不想再等了,也可以退出.

     加锁和释放锁的原理

      现象

      每一个类的实例对应一把锁,每次调用被synchronized修饰的方法都必须首先获得该方法所属调用者实例的锁.否则线程阻塞,

      方法一旦执行,该线程就独占了该锁知道该方法返回或者抛出异常,才能释放锁.

      获取和释放锁的时机:内置锁(监视器锁)

      等价代码

      

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Demo2 {
        private Lock lock = new ReentrantLock();
        public  synchronized  void method1(){
            System.out.println("我是synchronized锁住的方法");
        }
        public  void method2(){
            //加锁
            lock.lock();
            try {
                System.out.println("我是ReentranLock锁住的方法");
            }finally {
                lock.unlock();
            }
        }
        public static void main(String[] args) {
            Demo2 demo2 = new Demo2();
            demo2.method1();
            demo2.method2();
        }
    }

    synchronized的缺点

      1.效率低:锁的释放情况少,试图获得锁时不能设定超时,不能中断一个正在试图获得所得线程

      2.不够灵活(读写锁更灵活):加锁和释放的时机单一,每个锁仅有单一的条件(某个对象),可能是不够的

      3.无法知道是否成功获取到锁

  • 相关阅读:
    三分 例题
    设计模式学习笔记(十九):迭代器模式
    设计模式学习笔记(十八):解释器模式
    设计模式学习笔记(十七):命令模式
    设计模式学习笔记(十六):职责链模式
    设计模式学习笔记(十五):代理模式
    设计模式学习笔记(十四):享元模式
    设计模式学习笔记(十三):外观模式
    设计模式学习笔记(十二):装饰模式
    设计模式学习笔记(十一):组合模式
  • 原文地址:https://www.cnblogs.com/cg961107/p/10923114.html
Copyright © 2011-2022 走看看