synchronized 类锁 对象锁

本文深入探讨Java中的锁机制,区分类锁与对象锁的概念,并通过具体代码实例展示两种锁的不同行为特征及其对并发线程的影响。

分类

类锁:在代码中的方法上加了static和synchronized的锁,或者synchronized(xxx.class)的代码段。

对象锁:在代码中的方法上加了synchronized的锁,或者synchronized(this)的代码段。

代码

package com.example.shengshuqiang.helloworld;

/**
 * User: shengshuqiang
 * Date: 2015-08-31
 * Time: 15:25
 */
public class JavaTest {

    public static void main(String[] args) {
        Object lock = new Object();
        for (int i = 0; i < 5; i++) {
            System.out.println(i);
            new TestClassLockThread().start();
            new TestObjectLockThread().start();
            new TestOneObjectLockThread(lock).start();
        }
    }

    static class A {
        public static synchronized void classLock() {
            System.out.println("classLock start");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("classLock end");
        }

        public synchronized void objectLock() {
            System.out.println(this + "objectLock start");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this + "objectLock end");
        }

        public void oneObjectLock(Object lock) {
            synchronized (lock) {
                System.out.println(this + ", lock= " + lock + ", oneObjectLock start");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(this + ", lock= " + lock + ", oneObjectLock end");
            }
        }
    }

    static class TestClassLockThread extends Thread {
        @Override
        public void run() {
            A.classLock();
        }
    }

    static class TestObjectLockThread extends Thread {
        @Override
        public void run() {
            A a = new A();
            a.objectLock();
        }
    }

    static class TestOneObjectLockThread extends Thread {
        private Object lock;
        public TestOneObjectLockThread(Object lock) {
            this.lock = lock;
        }
        @Override
        public void run() {
            A a = new A();
            a.oneObjectLock(lock);
        }
    }
}

输出

0
classLock start
com.example.shengshuqiang.helloworld.JavaTest$A@7461e6f7objectLock start
1
com.example.shengshuqiang.helloworld.JavaTest$A@72116e25, lock= java.lang.Object@7cd68e17, oneObjectLock start
2
com.example.shengshuqiang.helloworld.JavaTest$A@69ab3312objectLock start
3
com.example.shengshuqiang.helloworld.JavaTest$A@47420a89objectLock start
4
com.example.shengshuqiang.helloworld.JavaTest$A@3ff76fb9objectLock start
com.example.shengshuqiang.helloworld.JavaTest$A@20fc4b96objectLock start
classLock end
com.example.shengshuqiang.helloworld.JavaTest$A@72116e25, lock= java.lang.Object@7cd68e17, oneObjectLock end
com.example.shengshuqiang.helloworld.JavaTest$A@7461e6f7objectLock end
com.example.shengshuqiang.helloworld.JavaTest$A@69ab3312objectLock end
com.example.shengshuqiang.helloworld.JavaTest$A@20fc4b96objectLock end
com.example.shengshuqiang.helloworld.JavaTest$A@47420a89objectLock end
com.example.shengshuqiang.helloworld.JavaTest$A@3ff76fb9objectLock end
com.example.shengshuqiang.helloworld.JavaTest$A@615608be, lock= java.lang.Object@7cd68e17, oneObjectLock start
classLock start
classLock end
com.example.shengshuqiang.helloworld.JavaTest$A@615608be, lock= java.lang.Object@7cd68e17, oneObjectLock end
classLock start
com.example.shengshuqiang.helloworld.JavaTest$A@694f64d2, lock= java.lang.Object@7cd68e17, oneObjectLock start
classLock end
com.example.shengshuqiang.helloworld.JavaTest$A@694f64d2, lock= java.lang.Object@7cd68e17, oneObjectLock end
classLock start
com.example.shengshuqiang.helloworld.JavaTest$A@286b2962, lock= java.lang.Object@7cd68e17, oneObjectLock start
classLock end
com.example.shengshuqiang.helloworld.JavaTest$A@286b2962, lock= java.lang.Object@7cd68e17, oneObjectLock end
classLock start
com.example.shengshuqiang.helloworld.JavaTest$A@5922fa5a, lock= java.lang.Object@7cd68e17, oneObjectLock start
classLock end
com.example.shengshuqiang.helloworld.JavaTest$A@5922fa5a, lock= java.lang.Object@7cd68e17, oneObjectLock end

结论

锁的本质是判断是否锁住同一个东西,如果竞争的是同一个锁,则互斥。如果不是同一个锁,则相互不影响。
类锁是锁住该类,全局只有一个类A,所以线程对该类的锁获取是互斥的。一定是先输出“class lock start”,然后“class lock end”,即执行“class lock start”后,别的线程需要等待该方法执行完“class lock end”,才可以接着执行。必须要等到类A的锁释放后才可以重新获取。
对象锁是锁住该对象,
如果竞争的不是同一个对象,则对象锁的获取相互不影响。上面输出可以看出,连续两个“object lock start”对应的对象this不一样。
如果竞争的是同一个对象,则对象锁的获取互斥。上面输出可以看出,“oneObject lock start”oneObject lock end”顺序出行,在执行“oneObject lock start”后,想要获取该对象锁lock的其他线程必须等待lock锁释放后“oneObject lock end”

参考链接

http://ifeve.com/java-locks/

<think> 首先,用户的问题是:"我想查找synchronized对象选择的相关知识 请问synchronized对象选择的原则和方法"。这是关于Java synchronized关键字中对象的选择。 从系统指令来看,我需要: - 使用$...$格式行内数学表达式(但这个问题是关于Java的,可能不需要数学,除非涉及到算法效率等)。 - 独立公式用$$...$$,但这里可能不适用。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,如[^1]。 用户提供了几个引用: - [^1]:建议选择引用不会变化的对象(如final),避免意想不到的后果。对于需要不同对象的情况,可以为每个对象构建一个Object。 - [^2]:展示了synchronized的用法,包括对象锁(用this或实例对象)和静态方法(用对象)。 - [^3]:解释了synchronized修饰方法和代码块的等价形式,包括非静态方法this,静态方法Class对象。 - [^4]:比较Lock和synchronizedsynchronized自动释放。 - [^5]:讨论了对象的唯一性,使用唯一如final变量。 核心主题是synchronized对象的选择原则和方法。 我需要基于这些引用构建清晰、结构化的回答。步骤: 1. **介绍对象的重要性**:synchronized需要指定对象。 2. **原则**: - 对象应该是不可变的引用(如final对象),以防止引用变化导致问题。 - 确保对象的唯一性和一致性。 3. **方法**: - 对象锁:使用this或实例对象。 - :使用Class对象。 - 自定义:为每个对象创建专用对象。 - 避免可变引用。 4. **示例**:从引用中提取代码示例。 5. **其他注意事项**:如的粒度、性能影响。 6. **相关问题**:生成2-3个相关问题。 回答结构: - 开头简述重点。 - 分点解释原则和方法。 - 提供代码片段(如果需要)。 - 结尾添加相关问题。 引用标识:在解释中自然地添加引用,如"[^1]"。 最终输出: - 使用中文。 - 清晰分段的回答。 - 生成相关问题。 </think> ### synchronized对象选择的原则和方法 在Java中,`synchronized`关键字用于实现线程同步,其核心是通过指定一个对象来控制临界区的访问。选择适当的对象是实现高效、安全线程同步的关键。以下是根据引用内容总结的原则和方法,确保对象的选择合理且可靠。 #### 一、对象选择的原则 1. **引用不可变性原则**:对象的引用应该尽量不可变(例如使用`final`修饰符)。如果引用发生变化,可能导致失效或意外行为,因为`synchronized`需要通过引用定位对象。引用变化可能使多个线程误入同一临界区,破坏同步。[^1] 2. **唯一性与一致性原则**:对象应具有唯一性,确保同一时间只有一个线程能访问共享资源。避免使用可变对象作为(如频繁修改的字符串或自定义对象),否则可能引入并发问题。理想情况下,每个独立对象应有专用。[^5] 3. **粒度控制原则**:对象的选择应匹配资源的访问粒度。对于实例级资源,使用对象锁(如`this`);对于级资源,使用(如`ClassName.class`)。避免过大或过小的粒度,以减少线程竞争或上下文切换开销。[^2][^3] 4. **避免外部干扰原则**:不要使用可能被外部代码修改的对象作为(如公共集合)。优先选择私有对象或内部,以防止死或性能下降。[^1][^4] #### 二、对象选择的方法 1. **对象锁(实例级)**: - **方法**:使用当前实例(`this`)作为对象。适用于对实例变量进行同步。 - **示例代码**:引用[2]和[3]中展示了如何用`synchronized`代码块或方法修饰符实现。 ```java public class Test { // 方法等价于 synchronized(this) public synchronized void instanceMethod() { // 临界区代码 } // 代码块显式使用 this public void blockMethod() { synchronized(this) { // 临界区代码 } } } ``` - **优点**:简单易用,自动管理的获取和释放。[^4] - **注意**:确保实例引用稳定。如果对象可能被重建,改用专用。[^1] 2. **)**: - **方法**:使用的`Class`对象作为(如`Test.class`)。适用于静态方法或级资源的同步。 - **示例代码**:引用[3]说明了静态方法的等价形式。 ```java public class Test { // 静态方法等价于 synchronized(Test.class) public static synchronized void staticMethod() { // 临界区代码 } public static void blockMethod() { synchronized(Test.class) { // 临界区代码 } } } ``` - **优点**:防止多个实例间的干扰,适合共享变量。[^3] 3. **自定义专用**: - **方法**:为每个需要同步的对象创建私有、不可变的对象(如`final Object lock = new Object()`)。引用[1]和[5]建议使用此方法处理可变对象或需要多个对象的情况。 - **示例代码**:引用[5]演示了如何确保唯一性。 ```java public class Run implements Runnable { private final Object lock = new Object(); // 唯一且不可变的对象 private int count = 0; @Override public void run() { synchronized(lock) { // 使用专用 // 临界区代码 count++; } } } ``` - **优点**:避免引用变化问题,增强可控性;适用于复杂场景如模块化同步。[^1][^5] - **注意**:对象应为私有,防止外部访问。 4. **避免对象变化的方法**: - 使用基本型封装(如`final int lockId = 0; synchronized((Integer)lockId)`),但需注意装箱对象的唯一性保证。[^5] - 优先选择`synchronized`方法修饰符减少手动管理,但方法体过大时应拆分为代码块以维护可读性。[^1][^3] #### 三、注意事项 - **引用变化的后果**:如果对象引用变化(如重新赋值),可能导致线程获取不同,破坏同步。这是为什么优先选择`final`对象的原因。[^1] - **与Lock接口的比较**:`synchronized`自动释放,但不如`Lock`灵活(如不支持中断或公平)。在需要高级特性时,可考虑替换为`Lock`。[^4] - **性能考量**:对象选择影响性能。对象锁粒度小,竞争少;粒度大,可能成为瓶颈。测试不同场景以优化。[^2][^4] 通过遵循以上原则和方法,您可以有效避免并发问题,提升代码的线程安全性。在实际应用中,始终通过单元测试和多线程环境验证行为。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值