文档章节

什么是锁 ?类锁 对象锁 方法锁 有什么区别

喜欢美好的太阳
 喜欢美好的太阳
发布于 2017/08/27 15:18
字数 2541
阅读 211
收藏 1

什么是锁?

在多线程编程中,为了保证数据操作的一致性,操作系统引入了锁机制,用于保证临界区代码的安全。通过锁机制,能够保证在多核多线程环境中,在某一个时间点上,只能有一个线程进入临界区代码,从而保证临界区中操作数据的一致性。

所谓的锁,说白了就是内存中的一个整型数,拥有两种状态:空闲状态和上锁状态。加锁时,判断锁是否空闲,如果空闲,修改为上锁状态,返回成功;如果已经上锁,则返回失败。解锁时,则把锁状态修改为空闲状态。

对于“释放锁,获取锁” 所说的锁可以理解为权力,是否写入的权力。或者是一种令牌,获取到就能进行操作,没有获取到就得等着。

 

下面以Java中synchronized加锁的方式理解 类锁,对象锁,方法锁有什么区别

 

Java中用synchronized加锁的机制

synchronized 在修饰代码块的时候需要一个reference对象作为锁的对象,在修饰方法的时候默认是当前对象作为锁的对象,在修饰类时候默认是当前类的Class对象作为锁的对象. 

(**每个Java对象都可以用作一个实现同步的锁的对象(被锁)**

注意括号里后面被加粗的话,在部分博客和《Java并发编程实战》第20页,2.3.1 第二段都没写。

原话:“每个Java对象都可以用作一个实现同步的锁”

这是不对的。容易误导不容易理解,正确的意思应该是每个对象都可以作为锁的对象,也就是被锁。

 

方法锁(synchronized修饰方法时)

通过在方法声明中加入 synchronized关键字来声明 synchronized 方法。

synchronized 方法控制对类成员变量的访问: 
每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态,从而有效避免了类成员变量的访问冲突。

上面红字的解释:(同一实例的两个synchronized方法不可以被两个线程同时访问,因为对象锁被占用。 也就是说,同一时刻,同一实例的多个synchronized方法最多只能有一个被访问。)

对象锁(synchronized修饰方法或代码块)

  当一个对象中有synchronized method或synchronized block的时候调用此对象的同步方法或进入其同步区域时,就必须先获得对象锁。如果此对象的对象锁已被其他调用者占用,则需要等待此锁被释放。(方法锁也是对象锁)        
 
java的所有对象都含有1个互斥锁,这个锁由JVM自动获取和释放。线程进入synchronized方法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized方法正常返回或者抛异常而终止,JVM会自动释放对象锁。这里也体现了用synchronized来加锁的1个好处,方法抛异常的时候,锁仍然可以由JVM来自动释放。 

对象锁的两种形式:

public class Test
{
    // 对象锁:形式1(方法锁)
    public synchronized void Method1()
    {
        System.out.println("我是对象锁也是方法锁");
        try
        {
            Thread.sleep(500);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }

    }

    // 对象锁:形式2(代码块形式)
    public void Method2()
    {
        synchronized (this)
        {
            System.out.println("我是对象锁");
            try
            {
                Thread.sleep(500);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }

    }
 }

类锁(synchronized 修饰静态的方法或代码块)

  由于一个class不论被实例化多少次,其中的静态方法和静态变量在内存中都只有一份。所以,一旦一个静态的方法被申明为synchronized。此类所有的实例化对象在调用此方法,共用同一把锁,我们称之为类锁。   
 
对象锁是用来控制实例方法之间的同步,类锁是用来控制静态方法(或静态变量互斥体)之间的同步。  
 
类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。  
java类可能会有很多个对象,但是只有1个Class对象,也就是说类的不同实例之间共享该类的Class对象。Class对象其实也仅仅是1个java对象,只不过有点特殊而已。由于每个java对象都有1个互斥锁,而类的静态方法是需要Class对象。所以所谓的类锁,不过是Class对象的锁而已。获取类的Class对象有好几种,最简单的就是[类名.class]的方式。

public class Test
{
   // 类锁:形式1
    public static synchronized void Method1()
    {
        System.out.println("我是类锁一号");
        try
        {
            Thread.sleep(500);
        } catch (InterruptedException e)
        {
            e.printStackTrace();
        }

    }

    // 类锁:形式2
    public void Method2()
    {
        synchronized (Test.class)
        {
            System.out.println("我是类锁二号");
            try
            {
                Thread.sleep(500);
            } catch (InterruptedException e)
            {
                e.printStackTrace();
            }

        }

    }
}

如果上边理解下面不用看就行,下面是另外两篇博客的内容,可以帮助理解

 

通俗解释:

假设我有一个类ClassA,其中有一个方法synchronized methodA(),那么当这个方法被调用的时候你获得就是对象锁。

但是要注意,如果这个类有两个实例,比如:

ClassA a = new ClassA();

ClassA b = new ClassA();

那么如果你在a这对象上调用了methodA,不会影响b这个对象,也就是说对于b这个对象,他也可以调用methodA,因为这是两对象,所以说对象锁是针对对象的。

而类锁,其实没有所谓的类锁,因为类锁实际上就是这个类的对象的对象锁,还是举例,我有一个类ClassA,其中有一个方法synchronized static methodA(),注意这个方法是静态的了,那就是说这个类的所有的对象都公用一个这个方法了,那如果你在这个类的某个对象上调用了这个方法,那么其他的对象如果想要用这个方法就得等着锁被释放,所以感觉就好像这个类被锁住了一样。

 

另外一篇博客的解释:

对象锁(方法锁),是针对一个对象的,它只在该对象的某个内存位置声明一个标识该对象是否拥有锁,所以它只会锁住当前的对象,一般一个对象锁是对一个非静态成员变量进行synchronized修饰,或者对一个非静态成员方法进行synchronized进行修饰,对于对象锁,不同对象访问同一个被synchronized修饰的方法的时候不会阻塞

类锁是锁住整个类,当有多个线程来声明这个类的对象时候将会被阻塞,直到拥有这个类锁的对象被销毁或者主动释放了类锁,这个时候在被阻塞的线程被挑选出一个占有该类锁,声明该类的对象。其他线程继续被阻塞住。

无论是类锁还是对象锁,父类和子类之间是否阻塞没有直接关系。当对一个父类加了类锁,子类是不会受到影响的,相反也是如此。因为synchronized关键字并不是方法签名的一部分,它是对方法进行修饰的。当子类覆写父类中的同步方法或是接口中声明的同步方法的时候,synchronized修饰符是不会被自动继承的,所以相应的阻塞问题不会出现。

注意:这里的阻塞问题是指的按照正常情况下应该阻塞,而因为synchronized是父类与子类之间不可传递导致不会阻塞。那正常情况下阻塞是什么那,下面会详细介绍。但是,当一个子类没有覆盖父类的方法的时候,这时候通过子类访问方法则会产生阻塞。

插入一句:构造方法不可能是真正同步的(尽管可以在构造方法中使用同步块)。

 

对于类锁,则会把整个类锁住,也就说只能有一个对象拥有当前类的锁。当一个对象拥有了类锁之后,另外一个对象还想竞争锁的话则会被阻塞。两个对象A,B,如果A正在访问一个被类锁修饰的方法function,那么B则不能访问。因为类锁只能在同一时刻被一个对象拥有。相对于对象锁,则是不同。还是A,B两个对象,如果A正在访问对象锁修饰的function,那么这个时候B也可以同时访问。

对于对象锁,当一个对象拥有锁之后,访问一个加了对象锁的方法,而该方法中又调用了该类中其他加了对象锁的方法,那么这个时候是不会阻塞住的。这是java通过可重入锁机制实现的。可重入锁指的是当一个对象拥有对象锁之后,可以重复获取该锁。因为synchronized块是可重入的,所以当你访问一个对象锁的方法的时候,在该方法中继续访问其他对象锁方法是不会被阻塞的。

 

参考来自!

操作系统中锁的实现原理

[Java] 方法锁、对象锁和类锁的意义和区别

synchronized&synchronized(something)与对象锁

“类锁”和对象锁

方法锁、对象锁、类锁

© 著作权归作者所有

喜欢美好的太阳

喜欢美好的太阳

粉丝 37
博文 657
码字总数 185538
作品 0
通州
程序员
私信 提问
synchronized和ReentrantLock区别

一.什么是sychronized sychronized是java中最基本同步互斥的手段,可以修饰代码块,方法,类. 在修饰代码块的时候需要一个reference对象作为锁的对象. 在修饰方法的时候默认是当前对象作为锁的对...

Lubby
2015/05/04
5.3K
0
多线程相关面试题及其解答

Q:什么是线程?什么是进程?线程和进程有什么区别? A:线程:又称"轻量级进程",是操作系统能够进行运算调度的最小单位,被包含在进程之中,是进程中的实际运作单位。 进程: 系统进行资源...

TinyDolphin
2017/12/17
0
0
sleep( ) 和 wait( ) 的这 5 个区别,你知道几个?

sleep(休眠) 和 wait(等待) 方法是 Java 多线程中常用的两个方法,它们有什么区别及一些该注意的地方有哪些呢?下面给大家一一分解。 区别1:使用限制 使用 sleep 方法可以让让当前线程休眠,...

Java技术栈
2018/09/19
0
0
43道多线程面试题,附带答案(三)

1.volatile关键字在Java中有什么作用? volatile是一个特殊的修饰符,只有成员变量才能使用它。 在Java并发程序缺少同步类的情况下,多线程对成员变量的操作对其它线程是透明的。 volatile变...

李红欧巴
07/12
73
0
java并发编程--互斥锁, 读写锁及条件

java.util.concurrent.locks包提供了锁和等待条件的接口和类, 可用于替代JDK1.5之前的同步(synchronized)和监视器机制(主要是Object类的wait(), notify(), notifyAll()方法). 互斥锁--Lock接...

真爱2015
2016/06/21
53
0

没有更多内容

加载失败,请刷新页面

加载更多

PhotoShop 色调:理解直方图/RGB通道信息

一、直方图:图表的形式,展示图像像素分布的情况 1.平均值:表示平均亮度 2.标准偏差值:表示亮度值范围内的中间值 3.像素: 表示用于计算直方图的像素总数 4.色阶:显示指针下面的区域亮度...

东方墨天
13分钟前
4
0
wildfly(JBoss AS)应用服务器快速入门

什么是wildfly JBoss AS 从8版本起名为wildfly。Wildfly是一个开源的基于JavaEE的轻量级应用服务器。可以在任何商业应用中免费使用。 WildFly是一个灵活的、轻量的、强大管理能力的应用程序服...

程序新视界
37分钟前
2
0
Java集合类常见面试知识点总结

Java集合类学习总结 这篇总结是基于之前博客内容的一个整理和回顾。 这里先简单地总结一下,更多详细内容请参考我的专栏:深入浅出Java核心技术 https://blog.csdn.net/column/details/21930...

Java技术江湖
40分钟前
6
0
怎么用for循环打出爱心

先上效果图: 这是用*组成的爱心,下面讲讲思路: 首先这个图形可以拆分成三部分:第一部分是上面三行的两个梯形,第二部分是中间三行的长方形,第三部分是最下面的倒三角形。 其实图形拆分好...

INEVITABLE
46分钟前
4
0
用HttpUrlConnection伪造成HttpClient

https://www.jianshu.com/p/27ad06cc39d2

shzwork
52分钟前
4
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部