Java并发-synchronized使用方法

本文详细介绍了Java中`synchronized`关键字在实例方法、静态方法和同步代码块中的应用,通过示例展示了如何使用`synchronized`实现线程同步,以及不同情况下的锁行为。重点讲解了实例锁、静态类锁和对象锁的区别,以及它们在并发控制中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

synchronized 关键字介绍

  • Java 中的每个对象都可以把它当作一个同步锁来使用,这些 Java 内置的使用者看不到的锁被称为内部锁,也叫作监视器锁。
  • 代码在进入 synchronized 代码块前会自动获取内部锁,这时候其他线程访问该同步代码块时会被阻塞挂起。拿到内部锁的线程会在正常退出同步代码块或者抛出异常后或者在同步块内调用了该内置锁资源的 wait 系列方法时释放该内置锁。
  • 内置锁:即排它锁,也就是当一个线程获取这个锁后,其他线程必须等待该线程释放锁后才能获取该锁。

Tips:由于 Java 中的线程是与操作系统的原生线程一一对应的,所以当阻塞一个线程时,需要从用户态切换到内核态执行阻塞操作,这是很耗时的操作,而 synchronized 的使用就会导致上下文切换。

 synchronized 的三种使用方式

Java 中每一个对象都可以作为锁,这是 synchronized 实现同步的基础。synchronized 的三种使用方式如下:

  • 普通同步方法(实例方法)锁是当前实例对象 ,进入同步代码前要获得当前实例的锁;
  • 静态同步方法锁是当前类的 class 对象 ,进入同步代码前要获得当前类对象的锁
  • 同步方法块:锁是括号里面的对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

synchronized 作用于实例方法

场景设计

  • 创建两个线程,分别设置线程名称为 threadOne 和 threadTwo;
  • 创建一个共享的 int 数据类型的 count,初始值为 0;
  • 两个线程同时对该共享数据进行增 1 操作,每次操作 count 的值增加 1;
  • 对于 count 数值加 1 的操作,请创建一个单独的 increase 方法进行实现;
  • increase 方法中,先打印进入的线程名称,然后进行 1000 毫秒的 sleep,每次加 1 操作后,打印操作的线程名称和 count 的值;
  • 运行程序,观察打印结果。

package jvm.juc;

public class SynchroizedTest extends Thread{
    static int count = 0;

    public synchronized void increaseCount() {
        String thread = Thread.currentThread().getName();
        System.out.println("当前" + thread + "线程正在操作");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count += 1;
        System.out.println("当前" + thread + ":" +  count);
    }

    @Override
    public void run() {
        increaseCount();
    }


    public static void main(String[] args) {
        SynchroizedTest test = new SynchroizedTest();
        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test);
        t1.setName("threadOne");
        t2.setName("threadTwo");
        t1.start();
        t2.start();

    }

}

上述程序运行结果如下图所示

 Tips:仔细看 DemoTest test = new DemoTest () 这就话,我们创建了一个 DemoTest 的实例对象,对于修饰普通方法,synchronized 关键字的锁即为 test 这个实例对象。

 synchronized 作用于静态方法

暂时其他代码不变,然后main函数里面代码改成如下形式

public static void main(String[] args) {
        SynchroizedTest test = new SynchroizedTest();
        SynchroizedTest test2 = new SynchroizedTest();
        Thread t1 = new Thread(test);
        Thread t2 = new Thread(test2);
        t1.setName("threadOne");
        t2.setName("threadTwo");
        t1.start();
        t2.start();

    }

代码执行的结果如下图所示

 可以看出,两个线程同时进入到锁中

两个线程持有两个不同的锁,不会产生互相 block。相信讲到这里,同学对实例对象锁的作用也了解了,那么我们再次将 increase 方法进行修改,将其修改成静态方法,然后输出结果。

 结果如下,又得到了顺序执行

 结果分析:我们看到,结果又恢复了正常,为什么会这样?
关键的原因在于,synchronized 修饰静态方法,锁为当前 class,即 DemoTest.class。

synchronized 作用于同步代码块 

Tips:对于 synchronized 作用于同步代码,锁为任何我们创建的对象,只要是个对象即可,如 new Object () 可以作为锁,new String () 也可作为锁,当然如果传入 this,那么此时代表当前对象。

	/**
     * synchronized 修饰实例方法
     */
    static final Object objectLock = new Object(); //创建一个对象锁
    public static void increase() throws InterruptedException {
        System.out.println(Thread.currentThread().getName() + "获取到锁,其他线程在我执行完毕之前,不可进入。" );
        synchronized (objectLock) {
            sleep(1000);
            count++;
            System.out.println(Thread.currentThread().getName() + ": " + count);
        }
    }

 代码解析:我们创建了一个 objectLock 作为对象锁,除了第一句打印语句,让后三句代码加入了 synchronized 同步代码块,当 threadOne 进入时,threadTwo 不可进入后三句代码的执行。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

兜兜转转m

一毛钱助力博主实现愿望

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值