Q1.一个类中同时有synchronized和非synchronized方法,一个线程调用synchronized方法时,另一个线程能不能调用非synchronized方法?
A1.能。因为第二个线程调用非synchronized方法时,不需要申请堆内存里的锁。就像第一个线程去上厕所把门锁住了,第二个线程去厨房,不需要进厕所,所以可以调用。注意synchronized方法锁定的是调用该方法的对象。
Q2.synchronized获取的锁可重入吗?
A2.可重入。在一个线程中,调用一个synchronized方法,该方法中又调用另一个synchronized方法,再次申请锁的时候仍然可以得到锁,锁的次数从1变成2。同样的,子类synchronized方法可以调用父类synchronized方法获取锁。两次锁定的是同一个对象,都是this。
Q3.发生异常时会释放锁吗?
A3.发生异常时,默认会释放锁,必须小心处理异常。以银行转账为例,调用synchronized转账方法时,先从A的账户上扣去100元,再在B的账户上加上100元。如果多个线程对A账户调用转账方法,而第一个线程扣完100元后抛出了异常,如果不处理,锁会被释放,第二个线程获取到锁,再从A账户上扣去100元,C账户上加上100元,这样就会出现问题。
Q4.volatile关键字
A4.一个线程中的volatile变量发生改变时,会通知其它线程他们缓存中的这个变量值过期了,应当从主内存中再读一次。volatile关键字能保证线程之间变量的可见性,但不能保证操作的原子性,是一种无锁同步,效率比synchronized高得多,但不能替代synchronized。例如,定义全局的volatile int count,再定义add方法循环10000次,每次count++,起10个线程一起调用add方法,按照道理count最后会加到100000.但由于count++不是原子操作,虽然每个线程读count的时候都是正确的count,但多个线程都读到一个count值,count+1,再给count赋值,就发生了重复的情况。
Q5.String s1 = "hello"; String s2 = "hello"; synchronized(s1),synchronized(s2)为什么会死锁?
A5.因为锁是锁在堆内存上的,锁的是"hello"这个字符串对象,而不是s1,s2的引用。开发中不要锁定字符串常量,因为如果引用的jar包里也锁定了这个字符串常量,将产生死锁。并且这种死锁非常隐蔽,不容易debug出来。