Synchronize深入解析

阅读:246

目录介绍

  • 1.synchronized简单介绍
    • 1.1 简单介绍
    • 1.2 可以修饰哪些内容
  • 2.synchonized(this)和synchonized(object)区别
  • 3.sychonized method 和 synchonized代码块的效率问题
  • 4.代码使用案例
    • 4.1 第一个案例代码
    • 4.2 第二个案例代码【 同步代码块时】
    • 4.3 第三个案例代码【 synchronized对象锁 】
  • 5.结论
  • 6.Synchronize问题
    • 6.1 Synchronize作用于方法和静态方法区别

1.synchronized简单介绍

1.1 简单介绍

  • synchronized 是java语言关键字,当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。
  • synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块。
    //同步的方法
    pubilc synchronized void test() {
    
    }
    
    //同步代码块上
    public void test() {
         synchronized(obj) {
              System.out.println("===");
         }
    }
    

1.2 可以修饰哪些内容

    1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
    1. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
    1. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
    1. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

2.synchonized(this)和synchonized(object)区别

  • 其实并没有很大的区别,synchonized(object)本身就包含synchonized(this)这种情况,使用的场景都是对一个代码块进行加锁,效率比直接在方法名上加synchonized高一些(下面分析),唯一的区别就是对象的不同。
  • 对synchronized(this)的一些理解
    • 一、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。
    • 二、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
    • 三、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
    • 四、当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。

3.sychonized method 和 synchonized代码块的效率问题

  • 使用前者的对象会获取该对象中所有synchonized的锁,也就是其他线程不能访问所有加了synchonized前缀的方法(不仅仅只是当前运行的方法),影响了其他线程多其他同步方法的访问,降低了效率。而后者只对当前代码块加锁,其他的同步方法不受影响。

4.代码使用案例

4.1 第一个案例代码

  • 简单使用
    Thread01 t01 = new Thread01();
    System.out.println("synchronized 关键字使用 \n" +"--------------------------");
    Thread ta = new Thread(t01,"A");
    Thread tb = new Thread(t01,"B");
    ta.start();
    tb.start();
    
    private class Thread01 implements Runnable{
        @Override
        public void run() {
            synchronized (this) {
                for(int i=0;i<3;i++){
                    System.out.println(Thread.currentThread().getName()+" synchronized loop "+i);
                }
            }
        }
    }
    
    执行结果:
    synchronized 关键字使用
    --------------------------
    B synchronized loop 0
    B synchronized loop 1
    B synchronized loop 2
    A synchronized loop 0
    A synchronized loop 1
    A synchronized loop 2
    

4.2 第二个案例代码

  • 同步代码块
    System.out.println("synchronized 关键字使用 \n" +"--------------------------");
    Thread t02A = new Thread(new Runnable() {
        @Override
        public void run() {
            method01();
        }
    },"A");
    Thread t02B = new Thread(new Runnable() {
    
        @Override
        public void run() {
            method02();
        }
    },"B");
    Thread t02C = new Thread(new Runnable() {
        @Override
        public void run() {
            method3();
        }
    },"C");
    t02A.start();
    t02B.start();
    t02C.start();
    
    
    public void method01(){
        synchronized (this) {
            int i=0;
            while(i++ < 3){
                System.out.println(Thread.currentThread().getName() +":"+ i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public void method02(){
        //第2种方式:当一个线程访问object的一个synchronized(this)同步代码块时,
        //其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
        synchronized (this) {
            int j=0;
            while(j++ < 3){
                System.out.println(Thread.currentThread().getName() +":"+ j);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    /*
     * 当一个线程访问object的一个synchronized(this)同步代码块时,
     * 它就获得了这个object的对象锁。
     * 结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
     */
    public synchronized void method3(){
        int k=0;
        while(k++ < 3){
            System.out.println(Thread.currentThread().getName() +":"+ k);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    执行结果:
    synchronized 关键字使用
    --------------------------
    B:1
    B:2
    B:3
    C:1
    C:2
    C:3
    A:1
    A:2
    A:3
    

4.3 第三个案例代码【 synchronized对象锁 】

  • synchronized对象锁
    final InnerObject innerObj = new InnerObject();
    System.out.println("synchronized 关键字使用 \n" +"--------------------------");
    Thread t03A = new Thread(new Runnable() {
        @Override
        public void run() {
            outerMethod01(innerObj);
        }
    },"A");
    Thread t03B = new Thread(new Runnable() {
        @Override
        public void run() {
            outerMethod02(innerObj);
        }
    },"B");
    t03A.start();
    t03B.start();
    
    class InnerObject{
        private void innerMethod01(){
            int i=0;
            while(i++ < 3){
                System.out.println(Thread.currentThread().getName() +":"+ i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private void innerMethod02(){
            int j=0;
            while(j++ < 3){
                System.out.println(Thread.currentThread().getName() +":"+ j);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    /**
     * 外部类方法1
     */
    private void outerMethod01(InnerObject innerObj){
        synchronized (innerObj) {
            innerObj.innerMethod01();
        }
    }
    
    /**
     * 外部类方法2
     */
    private void outerMethod02(InnerObject innerObj){
        innerObj.innerMethod02();
    }
    
    执行结果:
    synchronized 关键字使用
    --------------------------
    A:1
    B:1
    B:2
    A:2
    B:3
    A:3
    

5.结论

  • 1.synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)。
  • 2.synchronized 块是这样一个代码块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

6.Synchronize问题

6.1 Synchronize作用于方法和静态方法区别

  • 测试代码如下所示
    private void test() {
        final TestSynchronized test1 = new TestSynchronized();
        final TestSynchronized test2 = new TestSynchronized();
        Thread t1 = new Thread(new Runnable() {
    
            @Override
            public void run() {
                test1.method01("a");
                //test1.method02("a");
            }
        });
        Thread t2 = new Thread(new Runnable() {
    
            @Override
            public void run() {
                test2.method01("b");
                //test2.method02("a");
            }
        });
        t1.start();
        t2.start();
    }
    
    private static class TestSynchronized{
        private int num1;
        public synchronized void method01(String arg) {
            try {
                if("a".equals(arg)){
                    num1 = 100;
                    System.out.println("tag a set number over");
                    Thread.sleep(1000);
                }else{
                    num1 = 200;
                    System.out.println("tag b set number over");
                }
                System.out.println("tag = "+ arg + ";num ="+ num1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        private static int  num2;
        public static synchronized void method02(String arg) {
            try {
                if("a".equals(arg)){
                    num2 = 100;
                    System.out.println("tag a set number over");
                    Thread.sleep(1000);
                }else{
                    num2 = 200;
                    System.out.println("tag b set number over");
                }
                System.out.println("tag = "+ arg + ";num ="+ num2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    //调用method01方法打印日志【普通方法】
    tag a set number over
    tag b set number over
    tag = b;num =200
    tag = a;num =100
    
    
    //调用method02方法打印日志【static静态方法】
    tag a set number over
    tag = a;num =100
    tag b set number over
    tag = b;num =200
    


  • 在static方法前加synchronized:静态方法属于类方法,它属于这个类,获取到的锁,是属于类的锁。
  • 在普通方法前加synchronized:非static方法获取到的锁,是属于当前对象的锁。
  • 结论:类锁和对象锁不同,synchronized修饰不加static的方法,锁是加在单个对象上,不同的对象没有竞争关系;修饰加了static的方法,锁是加载类上,这个类所有的对象竞争一把锁。

赞赏支持


精彩留言

发表评论