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.无法知道是否成功获取到锁

 

posted @ 2019-05-29 23:13  简单爱1  阅读(6759)  评论(0编辑  收藏  举报