阿里内部面试总结

目录:

  1. StringBuffer和StringBuilder有什么区别?假设有一个方法,方法内部需要定义一个对象,可能是StringBuffer或StringBuilder,接下来会多次append操作,方法结束时,返回这个对象的toString()结果,并且这个线程会被多线程并发访问,请选择这个对象是被定义成StringBuffer或者StringBuilder?为什么?

  2. synchronized有什么用?如何使用?(伪代码,把所有使用方式都分别列出来)

  3. ReentranLock类有什么作用?它常用的方法有哪几个?分别有什么特点?

  4. 集群环境下多机器间进行同步操作有什么可选的解决方案?(最好用伪代码写出关键部分)

  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注解扫描是如何配置的?)

 

 

 

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注解扫描是如何配置的?)

 

附录:

[1] https://segmentfault.com/q/1010000005945389?_ea=960340 

[2] https://segmentfault.com/q/1010000005944096?_ea=959633

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值