类锁和对象锁的区别

本文深入探讨Java中的同步机制,包括对象锁与类锁的区别、同步方法与同步代码块的应用及优缺点,帮助读者理解如何有效地使用synchronized关键字。

在java编程中,经常需要用到同步,而用得最多的也许是synchronized关键字了,因为synchronized关键字涉及到锁的概念,所以先来了解一些相关的锁知识。


每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个有锁保护的同步代码块或方法。


java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。


下面分别分析这两种用法在对象锁和类锁上的效果。


public class TestSynchronized   
{    
    public synchronized void test1()   
    {    
              int i = 5;    
              while( i-- > 0)   
              {    
                   System.out.println(Thread.currentThread().getName() + " : " + i);    
                   try   
                   {    
                        Thread.sleep(500);    
                   }   
                   catch (InterruptedException ie)   
                   {    
                   }    
              }    
    }    
      
    public static synchronized void test2()   
    {    
         int i = 5;    
         while( i-- > 0)   
         {    
              System.out.println(Thread.currentThread().getName() + " : " + i);    
              try   
              {    
                   Thread.sleep(500);    
              }   
              catch (InterruptedException ie)   
              {    
              }    
         }    
    }    
      
    public static void main(String[] args)   
    {    
         final TestSynchronized myt2 = new TestSynchronized();    
         Thread test1 = new Thread(  new Runnable() {  public void run() {  myt2.test1();  }  }, "test1"  );    
         Thread test2 = new Thread(  new Runnable() {  public void run() { TestSynchronized.test2();   }  }, "test2"  );    
         test1.start();    
         test2.start();    
//         TestRunnable tr=new TestRunnable();  
//         Thread test3=new Thread(tr);  
//         test3.start();  
    }   
    
} 

运行结果:

test1 : 4  
test2 : 4  
test1 : 3  
test2 : 3  
test2 : 2  
test1 : 2  
test2 : 1  
test1 : 1  
test1 : 0  
test2 : 0 

上面代码synchronized同时修饰静态方法和实例方法,但是运行结果是交替进行的,这证明了类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。


到这里我们大致了解了类锁和对象锁的区别,接下来讲一下同步方法和同步代码块的区别。


public void test1()   
    {    
         synchronized(this)   
         {    
              int i = 5;    
              while( i-- > 0)   
              {    
                   System.out.println(Thread.currentThread().getName() + " : " + i);    
                   try   
                   {    
                        Thread.sleep(500);    
                   }   
                   catch (InterruptedException ie)   
                   {    
                   }    
              }    
         }    
    }    
      
    public synchronized void test2()   
    {    
         int i = 5;    
         while( i-- > 0)   
         {    
              System.out.println(Thread.currentThread().getName() + " : " + i);    
              try   
              {    
                   Thread.sleep(500);    
              }   
              catch (InterruptedException ie)   
              {    
              }    
         }    
    } 

我们看到这两个方法前面一个用的同步代码块,后面一个用的同步方法的方式,其实这两个效果是一样的,拿的都是同一个对象锁。那么问题来了,既然效果一样,那为什么要弄两种写法?这个问题也是synchronized的缺陷所在


synchronized的缺陷:当某个线程进入同步方法获得对象锁,那么其他线程访问这里对象的同步方法时,必须等待或者阻塞,这对高并发的系统是致命的,这很容易导致系统的崩溃。如果某个线程在同步方法里面发生了死循环,那么它就永远不会释放这个对象锁,那么其他线程就要永远的等待。这是一个致命的问题。


当然同步方法和同步代码块都会有这样的缺陷,只要用了synchronized关键字就会有这样的风险和缺陷。既然避免不了这种缺陷,那么就应该将风险降到最低。这也是同步代码块在某种情况下要优于同步方法的方面。例如在某个类的方法里面:这个类里面声明了一个对象实例,SynObject so=new SynObject();在某个方法里面调用了这个实例的方法so.testsy();但是调用这个方法需要进行同步,不能同时有多个线程同时执行调用这个方法。

这时如果直接用synchronized修饰调用了so.testsy();代码的方法,那么当某个线程进入了这个方法之后,这个对象其他同步方法都不能给其他线程访问了。假如这个方法需要执行的时间很长,那么其他线程会一直阻塞,影响到系统的性能。

如果这时用synchronized来修饰代码块:synchronized(so){so.testsy();},那么这个方法加锁的对象是so这个对象,跟执行这行代码的对象没有关系,当一个线程执行这个方法时,这对其他同步方法时没有影响的,因为他们持有的锁都完全不一样。


还有一种情况,就是如果用synchronized同步的方法test里有一部分比较耗时的代码是不需要同步的,但同步方法是只有这个方法执行完了才会释放锁,那么其他线程就必须一直等待。而如果我们用synchronized同步代码块,将不需要同步的又耗时的代码放在同步代码块后面,那么等同步代码块释放锁的时候,其他线程就可以拿到锁执行其他同步方法而不需要等到test执行完。这也就是同步代码块的灵活之处。


<think>我们正在讨论Java中的synchronized关键字,特别是方法对象的区别。根据提供的引用,我们可以总结如下: 1. **方法方法)**: - 当synchronized修饰实例方法(非静态方法)时,它定的是当前对象实例(即this)。这意味着在同一时间,只有一个线程可以访问该对象的这个同步方法,其他线程如果要访问同一个对象的这个同步方法将被阻塞。 - 当synchronized修饰静态方法时,它定的是整个(即的Class对象)。这意味着在同一时间,只有一个线程可以访问该的任何静态同步方法,其他线程如果要访问该的静态同步方法将被阻塞。 2. **对象**: - 使用synchronized代码块时,我们可以指定一个对象作为。这个对象可以是任意对象(比如一个实例对象或一个的Class对象)。当线程进入同步代码块时,必须获得指定对象的,退出同步代码块时释放。 - 如果对象是实例对象,那么它实例方法的效果似,都是对同一个实例对象进行加。 - 如果对象是的Class对象,那么它静态方法的效果似,都是对进行加区别主要体现在: - **的范围不同**: - 实例方法对象锁)是针对同一个实例对象的,不同实例对象的同步方法互不影响。 - 静态方法)是针对整个的,无论有多少个实例对象,都只有一个。 - **对象的选择**: - 方法(无论是实例方法还是静态方法)是一种隐式的定方式,对象是固定的(实例方法this,静态方法Class对象)。 - 对象(通过代码块)则更灵活,可以选择任意的对象作为,这样可以实现更细粒度的控制。 根据引用[2]:对象锁是两种不同的,因此当多个线程同时访问同一(静态同步方法对象锁(非静态同步方法)时,这两个方法可以异步执行,互不阻塞。 示例说明: - 对象锁(实例方法): 如引用[5]中的代码,在实例方法`t`上加了synchronized,那么对于同一个`AdminController`实例(即`adminController`),多个线程调用`t`方法时,只能有一个线程进入执行,其他线程阻塞等待。 - (静态方法): 如果有一个静态方法synchronized修饰,那么无论创建多少个实例对象,多个线程中只有一个线程能执行该静态方法。 - 代码块对象: 例如: ```java public void method() { synchronized(this) { // 同步代码块住当前对象实例 } } ``` 或者 ```java public void method() { synchronized(MyClass.class) { // 同步代码块住整个 } } ``` 总结: - 方法:分为实例方法对象锁静态方法)。 - 对象:通过synchronized代码块指定对象,可以是任意对象,包括实例对象或对象。 - 区别在于的范围的粒度:对象锁特定实例,整个代码块可以更灵活地选择对象控制同步范围。 相关问题: 1. 在什么情况下应该使用,什么情况下使用对象锁? 2. synchronized修饰静态方法非静态方法同时存在时,它们之间会相互影响吗? 3. 如何避免synchronized造成的死问题? 4. 除了synchronized,Java中还有哪些实现线程同步的方式? </think>### synchronized方法对象的区别 在Java中,`synchronized`关键字用于实现线程同步,但**方法****对象**在作用范围实现方式上有本质区别: --- #### 1. **方法方法)** - **作用对象**: 当`synchronized`修饰**实例方法**(非静态方法)时,定的是**当前对象实例**(`this`);当修饰**静态方法**时,定的是**的Class对象**()[^1][^2][^4]。 - **特点**: - **实例方法**:多个线程访问**同一个对象**的同步方法时会互斥;但不同对象的同步方法互不影响[^5]。 ```java public synchronized void instanceMethod() { // 定当前实例(对象锁) } ``` - **静态方法**:定整个,所有线程访问该的**任一静态同步方法**时互斥[^2][^4]。 ```java public static synchronized void staticMethod() { // 的Class对象() } ``` - **示例**: 若线程A访问`obj1.instanceMethod()`,线程B访问`obj2.instanceMethod()`(不同对象),则不会互斥;但若两者访问同一个对象的同步方法,则互斥[^5]。 --- #### 2. **对象(代码块)** - **作用对象**: 通过`synchronized`代码块显式指定对象,可以是任意对象实例或的Class对象[^1][^4]。 ```java public void method() { synchronized (lockObject) { // 显式指定对象 // 临界区代码 } } ``` - **特点**: - **粒度更细**:可自由选择对象(如实例变量、静态变量、Class对象),灵活控制同步范围[^1][^4]。 - **避免方法的弊端**:例如只部分代码而非整个方法,减少线程阻塞时间。 - **方法的关联**: - `synchronized(实例对象)` ≈ 实例方法对象锁) - `synchronized(.class)` ≈ 静态方法) --- #### 关键区别 | **维度** | **方法** | **对象** | |------------------|---------------------------------------|-------------------------------------| | 作用范围 | 整个方法 | 代码块内部 | | 目标 | 隐式定`this`(实例方法)或`Class`对象(静态方法) | 显式指定任意对象 | | 灵活性 | 较低(无法细分范围) | 较高(可精确控制临界区) | | vs 对象锁 | 静态方法,实例方法对象锁 | 手动指定(`.class`)或对象锁 | --- #### 对象锁的互斥性 - ****:定整个,影响所有实例的静态方法[^2][^4]。 - **对象锁**:只定单个实例,不影响其他实例或静态方法[^5]。 - **两者关系**: 对象锁是**完全独立的**。线程A访问静态同步方法)时,线程B仍可访问同一对象的非静态同步方法对象锁),两者异步执行[^2]。 > 示例: > ```java > // (静态方法) > public static synchronized void classLock() { ... } > > // 对象锁(实例方法) > public synchronized void objectLock() { ... } > ``` > 多线程同时调用同一实例的`classLock()``objectLock()`不会互斥[^2]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值