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)
java类的概念{
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() { st1(); } }, "test1" );
Thread test2 = new Thread( new Runnable() { public void run() { st2(); } }, "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执⾏完。这也就是同步代码块的灵活之处。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论