java 类锁和对象锁_类锁和对象锁的区别

在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执行完。这也就是同步代码块的灵活之处。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值