synchronized 锁定的到底是什么

本文通过实例详细解析了Java中synchronized关键字的用法及其锁定机制,包括非静态方法、静态方法及类级别的锁,并总结了不同场景下的锁作用范围。


java 的锁,常用是synchronized,(当然还有Lock,后续如果有时间,会补充到后面),那么synchronized锁定的到底是什么呢?对象还是锁的是方法。

这里我暂时只说非static的例子,static的例子,后续会补充。


我们一般使用synchronized,以下两种

1)

synchronized void test(){}


2)

void test(){

synchronized (?){

}}


在实际应用中,还是需要了解synchronized 到底是什么的,这有助于我们处理对业务需求。为了解决synchronized 到底是什么,请看一下代码?

我写一个锁定方法的例子:

public class Main {

public synchronized void test(int k){
try {
System.out.println("Thread "+k +"  satrt");
Thread.sleep(10000);
System.out.println("Thread "+k +"  end");
} catch (InterruptedException e) {
}
}

}


在写一个线程类

class Test1 extends Thread{

private int num=0;

public Test1(int num) {
this.num = num;
}


@Override
public void run() {
new Main().test(num);
}

}


我在Main 类加主main方法:

public class Main {



public static void main(String[] args) {
new Test1(1).start();
new Test1(2).start();
new Test1(3).start();
}

}


执行结果:

Thread 1  satrt
Thread 3  satrt
Thread 2  satrt
Thread 2  end
Thread 3  end
Thread 1  end


可见synchronized 没有生效,都执行了。没有按照我们的想法去做。


下面我对 synchronized 方法进行下修改:

public void test(int k){
synchronized(this){
try {
System.out.println("Thread "+k +"  satrt");
Thread.sleep(10000);
System.out.println("Thread "+k +"  end");
} catch (InterruptedException e) {
}
}
}

在执行结果:

Thread 1  satrt
Thread 2  satrt
Thread 3  satrt
Thread 1  end
Thread 3  end
Thread 2  end

没有任何改变,和我们期望的不一致。


我们对上面的类进行一下修改:

public class Main {
public static void main(String[] args) {
Main main = new Main();

new Test1(1,main).start();
new Test1(2,main).start();
new Test1(3,main).start();
}

public synchronized void test(int k){
try {
System.out.println("Thread "+k +"  satrt");
Thread.sleep(10000);
System.out.println("Thread "+k +"  end");
} catch (InterruptedException e) {
}
}
}


对线程类也做一点修改:

class Test1 extends Thread{

private int num=0;
private Main main;

public Test1(int num,Main main) {
this.num = num;
this.main =main;
}


@Override
public void run() {
main.test(num);
}

}

执行下得到结果:

Thread 1  satrt
Thread 1  end
Thread 3  satrt
Thread 3  end
Thread 2  satrt
Thread 2  end

这是我们期望看到的结果。


由此得出结论: 对非static的synchronized 方法或者对象,都是只锁定该对象中的该方法,而不是锁定方法本身。


如果我们期望全局锁定方法,我们继续对上面的方法进行修改。

主类:

public class Main {



public static void main(String[] args) {
new Test1(1).start();
new Test1(2).start();
new Test1(3).start();
}

public static synchronized void test(int k){
try {
System.out.println("Thread "+k +"  satrt");
Thread.sleep(10000);
System.out.println("Thread "+k +"  end");
} catch (InterruptedException e) {
}
}
}


线程类:

class Test1 extends Thread{

private int num=0;

public Test1(int num) {
this.num = num;
}


@Override
public void run() {
test(num);
}

}


结果是:

Thread 1  satrt
Thread 1  end
Thread 3  satrt
Thread 3  end
Thread 2  satrt
Thread 2  end

这是我们期望的结果。


在做另外一种修改:

public class Main {



public static void main(String[] args) {
new Test1(1).start();
new Test1(2).start();
new Test1(3).start();
}

public void test(int k){
synchronized(Main.class){
try {
System.out.println("Thread "+k +"  satrt");
Thread.sleep(10000);
System.out.println("Thread "+k +"  end");
} catch (InterruptedException e) {
}
}
}
}


class Test1 extends Thread{

private int num=0;

public Test1(int num) {
this.num = num;
}


@Override
public void run() {
new Main().test(num);
}

}


结果是:

Thread 1  satrt
Thread 1  end
Thread 2  satrt
Thread 2  end
Thread 3  satrt
Thread 3  end


有由此我得出结论:我们期望全局锁定表,可以选择将方法变成static的synchronized这个类的class。


总结以上所得:

总结:
    sync(this) 锁定的是具体对象实例中的代码块。
    sync 函数 锁定的是具体对象实例中的函数代码块。
    sync(*.class) 锁定的是所有对象的实例,也就是所有该类构建的对象的全局锁。
    static sync 函数锁定的是所有对象实例的该函数代码块。


<think>我们正在讨论Java中synchronized关键字的锁定对象。根据引用[1]和引用[2]: - synchronized锁定的对象可以是实例对象或类对象(Class对象)。 - 当修饰实例方法时,锁定的是当前实例对象(this)。 - 当修饰静态方法时,锁定的是当前类的Class对象。 - 当修饰代码块时,可以指定锁定的对象(任意对象)。 因此,synchronized锁定的对象取决于使用方式: 1. 实例方法:锁定调用该方法的实例对象(this)。 2. 静态方法:锁定该类的Class对象(如MyClass.class)。 3. 代码块:锁定括号内指定的对象。 注意:每个对象都有一个与之关联的监视器(monitor),当线程进入synchronized代码块时,会尝试获取该对象的监视器,如果获取成功则执行代码,否则阻塞。 另外,引用[3]提到,在JVM中,对象的监视器由ObjectMonitor对象实现,它记录了的状态、持有的线程、等待队列等信息。 下面用代码示例说明: 示例1:实例方法 ```java public class Example { public synchronized void instanceMethod() { // 锁定的是当前实例对象,即this } } ``` 示例2:静态方法 ```java public class Example { public static synchronized void staticMethod() { // 锁定的是Example.class(Class对象) } } ``` 示例3:代码块 ```java public class Example { private final Object lock = new Object(); public void method() { synchronized (lock) { // 锁定的是lock对象 } } } ``` 重要规则: - 多个线程访问同一个对象的synchronized方法/代码块会互斥。 - 不同对象的synchronized方法/代码块不会互斥(因为对象不同)。 - 类和实例是不同的,因此不会互斥。 例如: 线程1:调用obj1的实例同步方法(锁定obj1) 线程2:调用obj2的实例同步方法(锁定obj2) -> 不会互斥,因为对象不同 线程3:调用类的静态同步方法(锁定类对象) -> 与上面两个都不互斥,因为对象不同 但是,如果多个线程同时访问同一个对象(或同一个类)的synchronized方法/代码块,则会发生互斥(阻塞等待)。 引用[1]提到:synchronized住的是一个对象或者类(类本身也是对象,即Class对象),而不是方法或者代码段。因此,的粒度是对象级别。 总结:synchronized锁定的对象取决于使用方式,具体为: - 实例方法:当前实例对象(this) - 静态方法:当前类的Class对象 - 代码块:显式指定的对象 这样,通过锁定不同的对象,可以实现不同粒度的同步控制。 相关问题: 1. 如何避免synchronized导致的死? 2. synchronized升级过程是怎样的? 3. 为什么说synchronized是非公平?</think>### Java中synchronized关键字的锁定对象详解 #### 一、锁定对象的本质 synchronized锁定的**不是方法或代码段**,而是**对象或类**(本质也是对象)。根据使用方式不同,锁定对象分为两类: 1. **实例对象**:锁定具体对象实例 2. **类对象**:锁定类的Class对象(如`MyClass.class`) #### 二、不同使用场景的锁定对象 | 使用方式 | 锁定对象 | 示例代码 | |------------------------------|--------------------------|--------------------------------------------------------------------------| | **修饰实例方法** | 当前实例对象 (`this`) | `public synchronized void method() {...}` [^1] | | **修饰静态方法** | 类的Class对象 | `public static synchronized void staticMethod() {...}` | | **代码块指定实例对象** | 显式指定的对象实例 | `synchronized(obj) {...}` | | **代码块指定Class对象** | 类的Class对象 | `synchronized(MyClass.class) {...}` | #### 三、互斥规则 1. **相同对象互斥**: ```java // 线程1和线程2访问同一个对象的同步方法会互斥 MyObject obj = new MyObject(); new Thread(obj::syncMethod).start(); // 线程1 new Thread(obj::syncMethod).start(); // 线程2 ❌ 阻塞 ``` 2. **不同对象不互斥**: ```java // 线程1和线程2访问不同对象的同步方法不互斥 MyObject obj1 = new MyObject(); MyObject obj2 = new MyObject(); new Thread(obj1::syncMethod).start(); // 线程1 new Thread(obj2::syncMethod).start(); // 线程2 ✅ 并行执行 ``` 3. **类与实例不互斥**: ```java class MyClass { public synchronized void instanceMethod() {...} // 实例 public static synchronized void staticMethod() {...} // 类 } // 可同时执行(对象不同) new Thread(() -> new MyClass().instanceMethod()).start(); new Thread(MyClass::staticMethod).start(); // ✅ 并行执行 ``` #### 四、底层实现原理 在JVM中,每个对象关联一个**监视器(Monitor)**,由`ObjectMonitor`实现(C++): ```cpp ObjectMonitor() { _count = 0; // 获取次数 _owner = NULL; // 持有的线程 _EntryList = NULL; // 阻塞线程队列 _WaitSet = NULL; // 调用wait()的线程队列 } ``` 当线程执行`synchronized`代码时: 1. 通过`monitorenter`指令尝试获取对象的监视器 2. 若`_owner`为null,则获得并设置`_owner`为当前线程 3. 若已被占用,则进入`_EntryList`阻塞等待[^3] #### 五、关键特性 1. **可重入性**:同一线程可重复获取同一把 ```java public synchronized void methodA() { methodB(); // ✅ 可重入 } public synchronized void methodB() {...} ``` 2. **粒度控制**: - 细粒度锁定独立对象减少竞争 ```java private final Object lock1 = new Object(); private final Object lock2 = new Object(); void method() { synchronized(lock1) { /* 操作资源1 */ } synchronized(lock2) { /* 操作资源2 */ } } ``` 3. **释放保证**: - 正常执行:通过`monitorexit`释放 - 异常退出:JVM确保释放(通过异常处理表) #### 六、使用建议 1. **避免字符串常量**:字符串常量池可能引发意外竞争 ```java // 危险操作(不同模块可能意外共享synchronized("GLOBAL_LOCK") {...} ``` 2. **优先选择私有对象**: ```java private final Object myLock = new Object(); // ✅ 安全 ``` 3. **类使用场景**: - 静态数据保护 - 类级别操作(如加载资源) > **关键原则**:的互斥范围取决于**对象的唯一性**,而非代码位置[^2]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值