学习笔记:java线程安全
class Thread2 extends Thread{ public void run() { MyObj obj = new MyObj(); try { obj.sayHello(3000);//3秒 } catch (InterruptedException e) { e.printStackTrace(); } } } class Thread3 extends Thread{ public void run() { MyObj obj = new MyObj(); try { obj.sayHello(1000);//1秒 } catch (InterruptedException e) { e.printStackTrace(); } } } class MyObj {
public synchronized void sayHello(int delay) throws InterruptedException{ Thread.sleep(delay); System.out.println("delay" + delay); } } public class ThreadTest { public static void main(String[] args) throws Exception{ Thread2 t2 = new Thread2(); Thread3 t3 = new Thread3(); t2.start();//先让t2线程启动,因为t2要等待3秒,如果线程同步有作用的话t3会处理阻塞状态 t3.start(); } }
代码中的MyObj类就是我用于测试的线程安全的对象,它包含了一个sayHello方法,他是带有synchronized关键字的。但测试结果却是
delay1000
delay3000
这说明关键字没有起作用,也说明了不同的对象实例synchronized方法时是线程不安全的。知道这个结果心里有些难过,只不过事情已经过去就当是学习了经验吧。
synchronized除了修饰方法外,还可以修饰代码块,那就试试看吧,写了一个新的类:
class MyObj2 { private static Object lockObj = new Object(); public void sayHello(int delay) throws InterruptedException{ synchronized(lockObj){ Thread.sleep(delay); System.out.println("delay" + delay); } } }
用这个类做测试的结果:
delay3000
delay1000
这说明synchronized修改代码块时线程同步是起作用的,但这里要注意,采用synchronized代码块时,synchronized(lockObj)中的lockObj对象是一个静态对象,所以他们对应的锁是同一个,这就可以实现线程间的同步。如果换成synchronized(this)又无法同步。
于是就得想明白为什么会有这两种差别呢?原来JAVA中每个对象都对应一个锁,synchronized关键字是通过检查这个对象锁的状态来调度的。这下就明白了,原来关键就在于对象对应的那个锁,MyObj之所以不能同步是因为创建了两不同的对象实例,自然对应的对象锁就不同,而synchronized修饰的是方法时,其对应检查的是当前对象的锁,所以就会出现不同步的情况。
后来在网上查资料的同时也发现一个叫类锁的东东,通过类锁获得的类本身,是唯一的,那么就应该是可以同步了,代码如下:
class MyObj3 { public synchronized static void sayHello(int delay) throws InterruptedException{ Thread.sleep(delay); System.out.println("delay" + delay); } }
得到的结果
delay3000
delay1000
这说明已经同步了,synchronized+static一起修改时获得的是类锁,获得类本身,所以只有一个,那么同步自然有了效果。
注:“光之子”在评论中指出了本文的一些问题,有兴趣的朋友看到本文请把评论也看看吧。