Java并发编程之Synchronized

1、简介

synchronized是Java中的关键字。
可以保证方法或者代码块在运行时,同一时刻只有一个方法可以进入到临界区,同时它还可以保证共享变量的内存可见性

2、synchronized有三种应用方式:

修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

示例1 不加 synchronized 处理

    //共享变量
    private static int inc = 0;
    private static final SynchronizedDemo demo = new SynchronizedDemo();

    private static void demo01()  {
        Thread t1 = new Thread(demo::method01, "t1");
        Thread t2 = new Thread(demo::method01, "t2");
        Thread t3 = new Thread(demo::method01, "t3");
        t1.start();
        t2.start();
        t3.start();

        // 线程礼让,子线程跑完,主线程才跑
        while (Thread.activeCount() > 1) Thread.yield();
        System.out.println("inc =" + inc);
    }

    private void method01() {
        common();
    }

    private static void common() {
        String thName = Thread.currentThread().getName();
        System.out.println("线程:" + thName + " is ready >> " + inc);
        try {
            Thread.sleep(1000);
            for (int i = 0; i < 1000; i++) {
                inc++;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程:" + thName + " is over >> " + inc);
    }

多次测试,不能保证最终结果一致
在这里插入图片描述

示例2 synchronized 修饰实例方法

    private static void demo02() {
        Thread t1 = new Thread(demo::method02, "t1");
        Thread t2 = new Thread(demo::method02, "t2");
        Thread t3 = new Thread(demo::method02, "t3");
        t1.start();
        t2.start();
        t3.start();

        while (Thread.activeCount() > 1) Thread.yield();
        System.out.println("inc =" + inc);
    }

    private synchronized void method02() {
        common();
    }

当多线程同时对一个对象的一个实例方法进行操作时,只有一个线程能够抢到锁。
因为一个对象只有一把锁,一个线程获取了该对象的锁之后,其他线程就无法获取该对象的锁了。
多次测试,能保证最终结果一致
在这里插入图片描述

示例3 synchronized 与 非synchronized 交叉调用

    private static void demo02_2() {
    	// method01 非synchronized
        Thread t1 = new Thread(demo::method01, "t1");
        // method02 synchronized 
        Thread t2 = new Thread(demo::method02, "t2");
        t1.start();
        t2.start();
        
        while (Thread.activeCount() > 1) Thread.yield();
        System.out.println("inc =" + inc);
    }

一个线程获取了该对象的锁之后,其他线程可以访问其他非synchronized实例方法,但无法访问其他synchronized实例方法
多次测试,不能保证最终结果一致
在这里插入图片描述

示例4 synchronized 作用于不同的实例对象

    private static void demo02_3() {
        SynchronizedDemo demo2 = new SynchronizedDemo();
        Thread t1 = new Thread(demo::method02, "t1");
        Thread t2 = new Thread(demo2::method02, "t2");

        t1.start();
        t2.start();

        while (Thread.activeCount() > 1) Thread.yield();
        System.out.println("inc =" + inc);
    }

因为两个线程作用于不同的对象,获得的是不同的锁,所以互相并不影响
多次测试,不能保证最终结果一致
在这里插入图片描述

示例5 synchronized 修饰静态方法

    private static void demo03() {
        Thread t1 = new Thread(SynchronizedDemo::method03, "t1");
        Thread t2 = new Thread(SynchronizedDemo::method03, "t2");
        Thread t3 = new Thread(SynchronizedDemo::method03, "t3");
        t1.start();
        t2.start();
        t3.start();

        while (Thread.activeCount() > 1) Thread.yield();
        System.out.println("inc =" + inc);
    }

    private static synchronized void method03() {
        common();
    }

作用于静态方法,此时的锁是类对象,所以实例对象共享同一把锁
多次测试,能保证最终结果一致在这里插入图片描述

示例6 synchronized 作用于代码块

private static void demo04() {
        Thread t1 = new Thread(demo::method04, "t1");
        Thread t2 = new Thread(demo::method04, "t2");
        Thread t3 = new Thread(demo::method04, "t3");
        t1.start();
        t2.start();
        t3.start();

        while (Thread.activeCount() > 1) Thread.yield();
        System.out.println("inc =" + inc);
    }

    private void method04() {
        String thName = Thread.currentThread().getName();
        System.out.println("线程:" + thName + " is ready >> " + inc);
        try {
            Thread.sleep(1000);
            synchronized (this) {
                System.out.println("线程:" + thName + " 持有锁");
                for (int i = 0; i < 1000; i++) {
                    inc++;
                }
                System.out.println("线程:" + thName + " 释放锁");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("线程:" + thName + " is over >> " + inc);
    }

多次测试,能保证最终结果一致
在这里插入图片描述

示例7 可重入性

    private static void demo05() {
        Thread t1 = new Thread(demo::method05, "t1");
        Thread t2 = new Thread(demo::method05, "t2");
        Thread t3 = new Thread(demo::method05, "t3");
        t1.start();
        t2.start();
        t3.start();
    }

    private synchronized void method05() {
        System.out.println("线程:" + Thread.currentThread().getName() + " 执行了method05");
        method05_2();
    }

    private synchronized void method05_2() {
        System.out.println("线程:" + Thread.currentThread().getName() + " 执行了method05_2");
    }

获取锁后进入synchronized同步代码,并在代码块中调用了当前实例对象的另外一个synchronized方法, 再次申请锁时,将被允许。这就是重入锁最直接的体现
在这里插入图片描述

示例8 继承之可重入

    private static void demo06() {
        SyncDemo sync = new SyncDemo();
        Thread t1 = new Thread(sync::method06, "t1");
        Thread t2 = new Thread(sync::method06, "t2");
        Thread t3 = new Thread(sync::method06, "t3");
        t1.start();
        t2.start();
        t3.start();
    }

    public synchronized void method06() {
        System.out.println("线程:" + Thread.currentThread().getName() + " 执行了父方法");
    }

    static class SyncDemo extends SynchronizedDemo {
        public synchronized void method06() {
            System.out.println("线程:" + Thread.currentThread().getName() + " 执行了子方法");
            super.method06();
        }
    }

当子类继承父类时,子类也是可以通过可重入锁调用父类的同步方法。
注意由于synchronized是基于monitor实现的,因此每次重入,monitor中的计数器仍会加1。
在这里插入图片描述

3、Synchronized的特点

1.Java中的关键字
2.原子性和可见性
3.自动获取、释放锁
4.可应用于代码块、实例方法、静态方法
5.可重入

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值