Java的synchronized使用小结

首先需要阐明两个基本问题:1.锁只能获取自对象,而不是代码,也就是说synchronized [static] methodName这样的写法其实获取的还是对象锁;2.未synchronized的代码执行不受锁的影响。

下面看synchronized的使用场景:

1.synchronized非静态方法和synchronized(this)

这是等效的两种写法,也就是说synchronized非静态方法其实获取的是this对象锁。

当多个线程操作同一实例时,任一线程调用任一synchronized非静态方法均会阻塞其余线程对所有synchronized非静态方法的调用,也就是说所有线程对synchronized非静态方法的调用将变成串行。当不同线程操作不同实例时,对synchronized非静态方法的调用将变成并行。

class SyncTest
{
    public void nonStaticA()
    {
        synchronized (this)
        {
            for (int i = 0; i < 5; ++i)
            {
                System.out.println(Thread.currentThread().getName() + " nonStaticA");
                Thread.sleep(100);
            }
        }
    }

    public synchronized void nonStaticB()
    {
        for (int i = 0; i < 5; ++i)
        {
            System.out.println(Thread.currentThread().getName() + " nonStaticB");
            Thread.sleep(100);
        }
    }

    ......
}
假设SyncTest对象obj,线程T1调用obj#nonStaticA,线程T2调用obj#nonStaticB,后者调用的执行必须在前者完成之后。

2.synchronized静态方法和synchronized(class)
这是等效的两种写法,也就是说synchronized静态方法其实获取的是class对象锁。
当多个线程操作任意实例时,任一线程调用任一synchronized静态方法均会阻塞其余线程对所有synchronized静态方法的调用,也就是说所有线程对synchronized静态方法的调用将变成串行。

class SyncTest
{
    public void staticA()
    {
        synchronized (SyncTest.class)
        {
            for (int i = 0; i < 5; ++i)
            {
                System.out.println(Thread.currentThread().getName() + " staticA");
                Thread.sleep(100);
            }
        }
    }

    public synchronized static void staticB()
    {
        for (int i = 0; i < 5; ++i)
        {
            System.out.println(Thread.currentThread().getName() + " staticB");
            Thread.sleep(100);
        }
    }

    ......
}
假设线程T1调用SyncTest#staticB,线程T2调用obj#staticA,后者调用的执行必须在前者完成之后。

3.synchronized非静态方法和synchronized静态方法共存
当多个线程操作同一实例时,任一线程调用任一synchronized非静态方法均不阻塞其余线程对所有synchronized静态方法的调用,反之亦然,也就是说这两者彼此不影响。
结合上述的SyncTest代码,假设线程T1调用obj#nonStaticB,线程T2调用SyncTest#staticB,两者会并发执行。

4.synchronized非静态成员变量
这个本质与1一样,只不过是锁的作用区域变小了,一般是方法体内的代码块。
当多个线程操作同一实例时,任一线程执行synchronized(lockObj)代码块时,均会阻塞其余线程对所有的synchronized(lockObj)代码块的执行,也就是说所有线程对synchronized(lockObj)代码块的执行将变成串行。注意:此处的synchronized(lockObj)表示获取名称为lockObj的非static对象的锁,如果类代码中还有其余对象锁的话,彼此不受影响。当然,如果不同线程针对不同实例的话,也相互没有影响。

class SyncTest
{
    private final Object lock = new Object();

    public void lockMethodA()
    {
	......

        synchronized (lock)
        {
            for (int i = 0; i < 5; ++i)
            {
                System.out.println(Thread.currentThread().getName() + " lockMethodA");
                Thread.sleep(100);
            }
        }

	......
    }

    ......
}
假设SyncTest对象obj,线程T1调用obj#lockMethodA,线程T2调用obj#lockMethodA,前者将在synchronized (lock)处阻塞后者。如果T2调用obj#nonStaticB,两个线程彼此没有影响。注意:obj#nonStaticB获取的是this对象锁,obj#lockMethodA获取的是lock对象锁,虽然lock是this的成员变量,但是作为对象锁时彼此没有影响。

5.synchronized静态成员变量
这个本质与2一样,也只不过是锁的作用区域变小为方法体内的代码块。
当多个线程操作任一实例时,任一线程执行synchronized(lockObj)代码块时,均会阻塞其余线程对所有的synchronized(lockObj)代码块的执行,也就是说所有线程对synchronized(lockObj)代码块的执行将变成串行。注意:此处的synchronized(lockObj)表示获取名称为lockObj的static对象的锁,如果类代码中还有其余对象锁的话,彼此不受影响。

class SyncTest
{
    private final static Object lockStaticA = new Object();

    private final static Object lockStaticB = new Object();

    public void lockStaticMethodA()
    {
	......

       synchronized (lockStaticA)
        {
            for (int i = 0; i < 5; ++i)
            {
                System.out.println(Thread.currentThread().getName() + " lockMethodA");
                try
                {
                    Thread.sleep(100);
                }
                catch (final InterruptedException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

	......
    }

    public void lockStaticMethodB()
    {
	......

        synchronized (lockStaticB)
        {
            for (int i = 0; i < 5; ++i)
            {
                System.out.println(Thread.currentThread().getName() + " lockStaticMethodB");
                try
                {
                    Thread.sleep(100);
                }
                catch (final InterruptedException e)
                {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

	......
    }

    ......
}
假设线程T1调用obj#lockStaticMethodA,线程T2调用obj1#lockStaticMethodA,前者将在synchronized(lockStaticA)处阻塞后者。但如果T2调用obj#lockStaticMethodB,将不受影响。

上面应该囊括了synchronized的绝大部分使用场景,不过这么累赘的描述其实可以简单的归类到两种大场景:获取锁的对象是静态还是非静态。如果是非静态,则只有操作同一实例的线程,在执行synchronized的代码区域时,会竞争指定的对象锁。如果是静态,则操作任一实例的线程,在执行synchronized的代码区域时,会竞争指定的对象锁。

最后再强调一下:不同的对象锁之间不受影响。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的synchronized关键字用于修饰方法或代码块,它能够保证在同一时刻最多只有一个线程执行该段代码。 通过在方法或代码块前使用synchronized关键字,可以实现对共享资源的互斥访问,避免多个线程同时修改共享资源导致的数据不一致问题。 使用synchronized修饰方法时,当一个线程进入该方法后,会获得该方法所属对象的锁,其他线程则需要等待锁的释放才能进入该方法。这样可以确保同一时刻最多只有一个线程执行该方法。 使用synchronized修饰代码块时,需要指定一个对象作为锁,当一个线程进入这个代码块时,会获得该锁,其他线程需要等待锁的释放才能进入代码块。这样可以确保同一时刻最多只有一个线程执行该代码块。 在多线程编程中,使用synchronized关键字可以有效地在多个线程之间实现同步,避免数据竞争和并发错误的发生。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [JavaSynchronized的用法(简单介绍)](https://blog.csdn.net/NeiHan2020/article/details/123277175)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [Javasynchronized的用法详解(四种用法)](https://blog.csdn.net/SimpleGZW/article/details/119866588)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值