目录:
-
StringBuffer和StringBuilder有什么区别?假设有一个方法,方法内部需要定义一个对象,可能是StringBuffer或StringBuilder,接下来会多次append操作,方法结束时,返回这个对象的toString()结果,并且这个线程会被多线程并发访问,请选择这个对象是被定义成StringBuffer或者StringBuilder?为什么?
-
synchronized有什么用?如何使用?(伪代码,把所有使用方式都分别列出来)
-
ReentranLock类有什么作用?它常用的方法有哪几个?分别有什么特点?
-
集群环境下多机器间进行同步操作有什么可选的解决方案?(最好用伪代码写出关键部分)
-
列出乐观锁的设计要点和使用方法?
-
何为幂等性控制?举一个例子说明你之前如何实现幂等性控制?(或在项目IDCM中如何实现幂等性控制?)
-
spring实现aop用到的关键技术是什么?
-
HashMap和ConcurrentHashMap有什么区别和特点?
-
java.util.concurrent package下,你用过哪些类?分别有什么用途和特点?
-
如果一张表数据量较大,影响了查询性能,可以有哪些优化方案?建立索引有什么原则?
-
说一说数据库事务隔离级别的理解?(项目IDCM中是如何使用的?)
-
Spring中注解@Component @Repository @Service @Controller的区别?(项目IDCM中context:component-scan注解扫描是如何配置的?)
1.StringBuffer和StringBuilder有什么区别?假设有一个方法,方法内部需要定义一个对象,可能是StringBuffer或StringBuilder,接下来会多次append操作,方法结束时,返回这个对象的toString()结果,并且这个线程会被多线程并发访问,请选择这个对象是被定义成StringBuffer或者StringBuilder?为什么?
答:StringBuffer是线程安全的;StringBuilder是线程不安全的。
1.先来看String StringBuffer StringBuilder定义。final修饰的类不能被继承,即不能拥有子类。 public final class StringBuffer public final class StringBuilder public final class String 2.关于appdend方法的源码如下: public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } public StringBuilder append(String str) { super.append(str); return this; } 3.对于经常变动的字符串才会考虑使用StringBuilder和StringBuffer,使用StringBuilder效率比StringBuffer高,StringBuffer可以保证线程安全,而StringBuilder不能。
2.synchronized有什么用?如何使用?(伪代码,把所有使用方式都分别列出来)
答:synchronized是Java语言的关键字,同时也是一个可重入锁。当它用来修饰一个方法或者一个代码块的时候,能够保证在同一时刻最多只有一个线程执行该段代码。synchronized用于修饰方法和代码块。
package basic;public final class TestSynchronized { public static void main(String[] args) { new Thread("线程A") { @Override public void run() { try { print("线程A ..."); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread("线程B") { @Override public void run() { try { print("线程B ..."); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } public static synchronized void print(String str) throws InterruptedException { System.out.println("当前线程:" + Thread.currentThread().getName() + "执行开始"); for (int i = 0; i < 10; i++) { System.out.println(str); Thread.sleep(2000); } System.out.println("当前线程:" + Thread.currentThread().getName() + "执行完毕"); } } // 代码执行结果: //当前线程:线程A执行开始 //线程A ... //线程A ... //线程A ... //线程A ... //线程A ... //线程A ... //线程A ... //线程A ... //线程A ... //线程A ... //当前线程:线程A执行完毕//当前线程:线程B执行开始 //线程B ... //线程B ... //线程B ... //线程B ... //线程B ... //线程B ... //线程B ... //线程B ... //线程B ... //线程B ... //当前线程:线程B执行完毕
synchronized在修饰方法的同时,还可以修饰代码块,示例代码如下:
package basic;public class SynchronizedExample { public static void main(String[] args) { new Thread("线程A") { @Override public void run() { print("线程A"); } }.start(); new Thread("线程B") { @Override public void run() { print("线程B"); } }.start(); } public static void print(String str) { System.out.println("线程: " + Thread.currentThread().getName() + "开始执行"); synchronized (SynchronizedExample.class) { for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "打印了信息:" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } System.out.println("线程: " + Thread.currentThread().getName() + "执行结束"); } }// 代码执行结果:// 线程: 线程A开始执行// 线程A打印了信息:0// 线程: 线程B开始执行// 线程A打印了信息:1// 线程A打印了信息:2// 线程A打印了信息:3// 线程A打印了信息:4// 线程A打印了信息:5// 线程A打印了信息:6// 线程A打印了信息:7// 线程A打印了信息:8// 线程A打印了信息:9// 线程: 线程A执行结束// 线程B打印了信息:0// 线程B打印了信息:1// 线程B打印了信息:2// 线程B打印了信息:3// 线程B打印了信息:4// 线程B打印了信息:5// 线程B打印了信息:6// 线程B打印了信息:7// 线程B打印了信息:8// 线程B打印了信息:9// 线程: 线程B执行结束
接下来详细说明synchronized在修饰方法的时候的细节。synchronized是对类的当前实例进行加锁,防止其他线程同时访问该类的该实例的所有synchronized块,注意这里是“类的当前实例”,类的两个不同实例就没有这种约束了。那么static synchronized恰好就是要控制类的所有实例的访问了,static synchronized是限制线程同时访问jvm中该类的所有实例同时访问对应的代码快。实际上,在类中某方法或某代码块中有synchronized,那么在生成一个该类实例后,该类也就有一个监视快,放置线程并发访问改实例synchronized保护快,而static synchronized则是所有该类的实例公用一个监视快了,也就是两个的区别了,也就是synchronized相当于this.synchronized,而static synchronized相当于Something.synchronized。
package basic;public class SynchronizedExample { public static void main(String[] args) { final MySynchronized mySynchronized = new MySynchronized(); new Thread("线程A") { @Override public void run() { mySynchronized.print("线程A"); } }.start(); new Thread("线程B") { @Override public void run() { mySynchronized.print("线程B"); } }.start(); } }class MySynchronized { public synchronized void print(String str) { System.out.println("线程: " + Thread.currentThread().getName() + "开始执行"); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "打印了信息:" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { } } System.out.println("线程: " + Thread.currentThread().getName() + "执行结束"); } } // 代码运行结果: //线程: 线程A开始执行 //线程A打印了信息:0 //线程A打印了信息:1 //线程A打印了信息:2 //线程A打印了信息:3 //线程A打印了信息:4 //线程A打印了信息:5 //线程A打印了信息:6 //线程A打印了信息:7//线程A打印了信息:8 //线程A打印了信息:9 //线程: 线程A执行结束 //线程: 线程B开始执行 //线程B打印了信息:0 //线程B打印了信息:1 //线程B打印了信息:2 //线程B打印了信息:3 //线程B打印了信息:4 //线程B打印了信息:5//线程B打印了信息:6 //线程B打印了信息:7 //线程B打印了信息:8 //线程B打印了信息:9 //线程: 线程B执行结束 package basic;public class SynchronizedExample { public static void main(String[] args) { final MySynchronized mySynchronized_first = new MySynchronized(); final MySynchronized mySynchronized_second = new MySynchronized(); new Thread("线程A") { @Override public void run() { mySynchronized_first.print("线程A"); } }.start(); new Thread("线程B") { @Override public void run() { mySynchronized_second.print("线程B"); } }.start(); } }class MySynchronized { public synchronized void print(String str) { System.out.println("线程: " + Thread.currentThread().getName() + "开始执行"); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "打印了信息:" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { } } System.out.println("线程: " + Thread.currentThread().getName() + "执行结束"); } }//代码运行结果: //线程: 线程A开始执行 //线程A打印了信息:0 //线程: 线程B开始执行 //线程B打印了信息:0 //线程A打印了信息:1 //线程B打印了信息:1 //线程A打印了信息:2 //线程B打印了信息:2 //线程A打印了信息:3//线程B打印了信息:3 //线程A打印了信息:4 //线程B打印了信息:4 //线程A打印了信息:5 //线程B打印了信息:5 //线程A打印了信息:6 //线程B打印了信息:6 //线程A打印了信息:7 //线程B打印了信息:7 //线程A打印了信息:8//线程B打印了信息:8 //线程A打印了信息:9 //线程B打印了信息:9 //线程: 线程A执行结束 //线程: 线程B执行结束 package basic;public class SynchronizedExample { public static void main(String[] args) { final MySynchronized mySynchronized_first = new MySynchronized(); final MySynchronized mySynchronized_second = new MySynchronized(); new Thread("线程A") { @Override public void run() { mySynchronized_first.print("线程A"); } }.start(); new Thread("线程B") { @Override public void run() { mySynchronized_second.print("线程B"); } }.start(); } }class MySynchronized { public static synchronized void print(String str) { System.out.println("线程: " + Thread.currentThread().getName() + "开始执行"); for (int i = 0; i < 10; i++) { System.out.println(Thread.currentThread().getName() + "打印了信息:" + i); try { Thread.sleep(1000); } catch (InterruptedException e) { } } System.out.println("线程: " + Thread.currentThread().getName() + "执行结束"); } }//代码运行结果: //线程: 线程A开始执行 //线程A打印了信息:0//线程A打印了信息:1 //线程A打印了信息:2 //线程A打印了信息:3 //线程A打印了信息:4 //线程A打印了信息:5 //线程A打印了信息:6 //线程A打印了信息:7//线程A打印了信息:8 //线程A打印了信息:9 //线程: 线程A执行结束 //线程: 线程B开始执行 //线程B打印了信息:0 //线程B打印了信息:1 //线程B打印了信息:2 //线程B打印了信息:3 //线程B打印了信息:4 //线程B打印了信息:5//线程B打印了信息:6 //线程B打印了信息:7 //线程B打印了信息:8 //线程B打印了信息:9 //线程: 线程B执行结束
上述代码完整的展示了static synchronized和synchronized的用法。synchronized针对同一个实例不能访问,针对不同的实例可以同时访问。static synchronized针对所有的实例均不能同时访问。synchronized本来就是修饰方法的,后来引申出synchronized修饰代码块,只是为了可以更精确的控制冲突限制的访问区域,使得表现更加高效率。synchronized方法只能锁定现阶段的对象,而synchronized区块可以锁定指定的对象,指定的对象直接跟在synchronized()括号之后。此外,synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法。还有synchronized不能被继承,继承时子类的覆盖方法必须显示定义成synchronized。
除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(object){/*区块*/},它的作用域是object对象。当一个线程执行时,将object对象锁住,另一个线程就不能执行对应的块。synchronized方法实际上等同于用一个synchronized块包住方法中的所有语句,然后在synchronized块的括号中传入this关键字。当然,如果是静态方法,需要锁定的则是class对象。可能一个方法中只有几行代码会涉及到线程同步问题,所以synchronized块比synchronized方法更加细粒度地控制了多个线程的访问,只有synchronized块中的内容不能同时被多个线程所访问,方法中的其他语句仍然可以同时被多个线程所访问(包括synchronized块之前的和之后的)。
package basic;public class TestSynchronizedObject { public static void main(String[] args) { final MyObject myObject_first = new MyObject(); final MyObject myObject_seconde = new MyObject(); new Thread("线程A") { @Override public void run() { myObject_first.print("线程A"); } }.start(); new Thread("线程B") { @Override public void run() { /* * 同一个实例,实现了互斥访问 */ myObject_first.print("线程B"); /* * 不同的实例,并不能够实现互斥访问 */ myObject_seconde.print("线程B"); } }.start(); } }class MyObject { /** * * synchronized(this)的用法相当于synchronized直接修饰方法<br/> * * 只针对一个实例的时候有效,针对多个实例的时候无效 */ public void print(String str) { System.out.println("线程" + Thread.currentThread().getName() + "开始执行"); synchronized (this) { for (int i = 0; i < 10; i++) { System.out.println(str + " ." + i + ". "); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } System.out.println("线程" + Thread.currentThread().getName() + "执行结束"); } }package basic;public class TestSynchronizedObject { public static void main(String[] args) { final MyObject myObject_first = new MyObject(); final MyObject myObject_seconde = new MyObject(); new Thread("线程A") { @Override public void run() { myObject_first.print("线程A"); } }.start(); new Thread("线程B") { @Override public void run() { myObject_seconde.print("线程B"); } }.start(); } }class MyObject { /** * * synchronized(MyObject.class)的用法相当于static synchronized修饰方法<br/> * * 在针对多个实例的情况下,互斥有效,但是synchronized括号后面要指定正确的对象信息 */ public void print(String str) { System.out.println("线程" + Thread.currentThread().getName() + "开始执行"); /* * 1、synchronized(MyObjcet.class)可以正确的实现互斥效果 ,因为调用的是MyObject的对象。 */ synchronized (MyObject.class) { for (int i = 0; i < 10; i++) { System.out.println(str + " ." + i + ". "); try { Thread.sleep(1000); } catch (InterruptedException e) { } } } System.out.println("线程" + Thread.currentThread().getName() + "执行结束"); } }
总结:synchronized方法是一种粗粒度的并发控制,在某一时刻,只能有一个线程执行该synchronized方法。synchronized块则是一种细粒度的鬓发控制,只会将块中的代码同步,位于方法内,synchronized块之外的代码是可以被多个线程同时访问到的。关于synchronized修饰代码块的详细细节可以参考附录[1]和附录[2]。
3.ReentrantLock类有什么作用?它常用的方法有哪几个?分别有什么特点?(读法:Re-entrantLock)
上面的的synchronized和ReentranLock是最经典的可重入锁,在面试中经常有问到两种锁的对比。先介绍ReentranLock的使用和代码示例,接着再分析ReentranLock和synchronized的具体区别。
4.集群环境下多机器间进行同步操作有什么可选的解决方案?(最好用伪代码写出关键部分)
reids
5.列出乐观锁的设计要点和使用方法?
6.何为幂等性控制?举一个例子说明你之前如何实现幂等性控制?(或在项目IDCM中如何实现幂等性控制?)
7.spring实现aop用到的关键技术是什么?
8.HashMap和ConcurrentHashMap有什么区别和特点?
9.java.util.concurrent package下,你用过哪些类?分别有什么用途和特点?
10.如果一张表数据量较大,影响了查询性能,可以有哪些优化方案?建立索引有什么原则?
11.说一说数据库事务隔离级别的理解?(项目IDCM中是如何使用的?)
12.Spring中注解@Component @Repository @Service @Controller的区别?(项目IDCM中context:component-scan注解扫描是如何配置的?)
附录: