请说明Java语言中的关键字synchronized的使用方法

简单的问题通常都不好回答,尤其在面试的时候,应对类似非常基础的问题时,应聘者通常很难抓住面试官提问的心理,假如回答稍有偏差,可能就会影响面试官对应聘者的印象,给面试的结果带来不确定性。

说起来Java的同步在使用上是非常简单的,具体而言可以概括为,同步方法和同步块。方法前加上synchronized,这个方法即是同步的,而使用synchronized(obj)和一对大括号包围住的代码块,则是同步块。听起来比较复杂、拗口,可能不好理解,那么见如下样例代码。

public class Test {

    private static final Object lock = new Object();

    private final Object lock2 = new Object();

    public synchronized void testA() {
        // some code
    }

    public static synchronized void testB() {
        // some code
    }

    public void testC() {
        synchronized (this) {
            // some code
        }
    }

    public void testD() {
        synchronized (lock) {
            // some code
        }
    }

    public void testE() {
        for (int i = 0; i < 10; ++i) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    synchronized (lock2) {
                        // some code
                    }
                }
            }).start();
        }
    }
}

样例中,testB是类的成员方法,testB是类的静态方法,testC、testD和testE演示了同步块的使用方法,区别在于testC的同步对象是对象自身,testD使用了类的静态成员,testE使用了对象的成员。

上述的几个样例最终都可以完成同步的需求,区别在于同步的范围和加锁的对象不同。根据实地编码的经验看,在不考虑死锁的情况下,同步的范围对代码运行效率有影响,在业务允许时一般范围越小越好。根据《深入浅出JVM》一书的介绍,Java语言中同步块的加锁对象有两类,类对象和类的实例对象。根据这两点可以对上述样例中的各个方法做个点评。

方法testA,类的成员方法,当某个实例在多线程中同时访问这个方法时,在某一具体时刻,只能有一个线程进入到这个方法内部,其它线程只能等待;由于testA在调用时需要绑定具体的对象实例,因而只有在多线程中访问同一个实例对象时,才会达到同步的效果;如果实例存在多个同步方法,当多个同步方法在多个线程中被调用时,由于锁定的对象是同一个,因而同一时刻,只有一个线程可以执行这个实例的某个方法,其它线程需要等待。

方法testB,类的静态方法,当在多个线程中同时调用这个方法时,在某一具体时刻,只能有一个线程进入到这个方法内部,其它线程只能等待;调用静态方法不需要绑定实例,因而与实例的同步方法调用不会出现同步现象。

方法testC,演示了代码块的使用方法,这里使用对象自身作为锁定对象,当这个对象在多个线程中共享访问这个同步块时会出现同步;同步范围变小,由整个方法缩小到了代码块内部。

方法testD,演示了使用类的静态对象作为锁定对象,当这个类的对象在多线程条件下访问testD方法内的同步块时,由于需要对类的静态对象加锁,这样即便不是相同的对象,仍然会达到同步的效果。

方法testE,最终达到的效果和方法testC相同,这种写法的优点是可以定义多个锁对象,这样不同的业务可以使用不同的加锁对象,在某种程度上可以更精确的控制加锁的范围,提高代码运行的效率。

总结一下,前面的描述比较罗嗦,知识点如下:

1、同步方法,包括类的静态方法和实例方法

2、同步代码块

3、构造函数不可以加同步关键字,这是一个语法错误。

4、子类覆盖父类的同步函数时,需要显式在覆盖后的方法前增加synchronized关键字,否则方法会失去同步的属性。

网上的资料很多,也非常深入,下面列出很少的一部分,非常感谢这些作者的分享。

http://gaojiewyh.iteye.com/blog/1775238

http://android.group.iteye.com/group/wiki/3083-java-sync-communication

http://blog.csdn.net/topwqp/article/details/8733063

http://fanning.iteye.com/blog/1449987

http://www.cnblogs.com/leizhilong/archive/2008/04/01/1133728.html

http://www.cnblogs.com/techyc/archive/2013/03/19/2969677.html

http://blog.csdn.net/super254/article/details/5055507

http://stackoverflow.com/questions/2144851/synchronization-in-vectors-in-java

http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html


前几天部门开会,听闻部门的小领导喜欢在面试的时候问应聘人synchronized关键字的用法,当时在座的兄弟都震惊了。这个关键字平时都在用,但具体到理论,可能就说不上了。感觉平时都忙干活了,被杂七杂八的事情围绕,业务需求虽然可以完成,但一些基础的东西时间长了不去接触,反而慢慢的都生疏了,这可不好,假如哪天有机会去面试新公司时被类似的问题问倒,岂不是对不起自己七、八年的工作经验和现任东家。

小领导举例说,有几个问题是他经常用的,以我面试应聘的兄弟时候,也可以拿来用用。

1、什么场景下使用synchronized关键字?

很显然,数据需要在多个线程中共享访问时,需要对数据进行保护,假如不存在这种场景,那么就不需要考虑多线程保护,否则会影响代码的运行效率。

2、这些场景下如果不使用synchronized关键字,会有什么后果?

多线程访问冲突,比如数据不一致、数据不正确,对容器操作时容易抛出并发修改异常。

3、vector是线程安全的吗?

简单场景下,vector是线程安全的,但在复杂场景下,仍然需要程序员使用额外的手段来保证同步,否则仍然存在不一致的可能。

。。。

你可能感兴趣的:(经验总结,Java)