学习笔记:java线程安全

首先得明白什么是线程安全:
线程安全是编程中的术语,指某个函数 (计算机科学)、函数库在多线程环境中被调用时,能够正确地处理各个线程的局部变量,使程序功能正确完成。
这是维基百科里的资料,看完后还不是特别的明白。我自己的理解就是在多线程环境下,某块代码中访问的资源不会发生冲突。


写这篇笔记的起因是上周的支付宝电话面试中问了我一个线程安全的问题,就是有一个类,他的方法A是加了synchronized关键字的,然后分别创建这个类的两个实例,请问,当多个线程同时访问这两个实例中的方法A时synchronized会起作用吗?

当时我的回答还是很明确而自信的说“会”,今天觉得这个问题要好好研究一下,于是就写了代码做了一下测试,发现自己答错了,这或许是我面试失败的原因之一吧。代码贴出来:
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一起修改时获得的是类锁,获得类本身,所以只有一个,那么同步自然有了效果。

你可能感兴趣的:(java线程)