JAVA题库(一)

1.多个线程同时读写,读线程的数量远远大于写线程,你认为应该如何解决并发的问题?你会选择什么样的锁?

答:解决高并发问题:选择ReadWriteLock读写锁。

public class ReadWriteLockTest {
    public static void main(String[] args) {
        final Queue queue = new Queue();
        for(int i =1;i<=3;i++){
            new Thread(new Runnable() {
                public void run() {
                    while(true){
                        try {
                            Thread.sleep((long)Math.random()*100000);
                            queue.put(new Random().nextInt(100000));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
            new Thread(new Runnable() {
                public void run() {
                    while(true){
                        try {
                            Thread.sleep((long)Math.random()*100000);
                            queue.get();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }
}
class Queue{
    //共享数据,只能有一个线程对其能更改
    private Object data = 85;
    ReadWriteLock rwl = new ReentrantReadWriteLock();
    public void get(){
        rwl.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " be ready to read data !");
            System.out.println(Thread.currentThread().getName() + " have read data :" + data);
        }finally{
            rwl.readLock().unlock();
        }
    }
    public void put(Object data){
        rwl.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " be ready to write data !");
            this.data = data ;
            System.out.println(Thread.currentThread().getName() + " have write data :" + data);
        }finally{
            rwl.writeLock().unlock();
        }
    }
}
2.JAVA的AQS是否了解,它是干嘛的?

答:AbstractQueuedSynchronizer,抽象的队列式的服务器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的ReentrantLock/Semaphore/CountDownLatch。

3.除了synchronized关键字之外,你是怎么来保障线程安全的?

答: 每次查询少查点,用rowid记录标记位,下次查询从标记位开始,就是个变相的分页。

4.Tomcat本身的参数你一般会怎么调整?

答:tomcat一些默认参数不适合生产环境使用,因此需要修改一些参数。
①.修改启动时内存参数,并指定JVM时区:

在Tomcat上运行j2ee项目代码时,经常会出现内存溢出的情况,解决办法是在系统参数中增加系统参数:

window下,在catalina.bat最前面:
set JAVA_OPTS=-XX:PermSize=64M -XX:MaxPermSize=128m -Xms512m -Xmx1024m;-Duser.timezone=GMT+08;一定加在catalina.bat最前面。

linux下,在catalina.sh最前面添加:
JAVA_OPTS=“-XX:PermSize=64M -XX:MaxPermSize=128m -Xms512m -Xmx1024m; -Duser.timezone=Asia/Shanghai”;一定要加在catalina.bat最前面;

注意:前后二者区别,有无set,有无双引号。

②.线程池配置:

使用线程池,用较少的线程处理较多的访问,可以提高Tomcat处理请求的能力,使用方式:
首先,打开/conf/server.xml,增加<Exector name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="500" minSpareThreads="20" maxldleTime="60000"/>
最大线程500,最小空闲线程数20,线程最大空闲时间60秒。
然后,修改<Connector ...>节点,增加executor属性,如:
<Connector    
exector="tomcatThreadPool" 
port="80" 
protocol="HTTP/1.1" 
maxThreads="600" 
minSpareThreads="100"  
maxSpareThreads="300"
connectionTimeout="60000"
keepAliveRequests="1"
redirectPort="443"/>
Tomcat可创建的最大线程数,每一个线程处理一个请求;
Tomcat启动时的初始化的线程数;
Tomcat就会关闭不再需要的socket线程;
connectionTimeout:网络连接超时,单位:毫秒。设置为0表示永不超时,这样设置有隐患的。通常设置为30000毫秒。
enableLookups:是否允许DNS查询
注意:可以多个connector公用一个线程池。

③.调整连接相关Connector的参数:

<Connector 
executor="tomcatThreadPool" 
port="80" 
protocol="HTTP/1.1"
connectionTimeout="60000"
keepAliveTimeout="15000"
maxKeepAliveRequests="1"
redirectPort="443"
maxHttpHeaderSize="8129" 
URIEncoding="UTF-8"
enableLookups="false" acceptCount="100"
disableUploadTimeout="true"/>

④.负载均衡,集群的配置
Tomcat6支持分布式部署,可以实现集群功能,提高相应能力
⑤.利用JMX监控Tomcat运行情况,需要手工调整启动参数

打开catalina.bat,增加一行
set JAVA_OPTS=%JAVA_OPTS%
-Dcom.sun.management.jmxremote.port=10090
-Dcom.sun.management.jmxremote.ssl=false 
-Dcom.sun.management.jmxremote.authenticate=false
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager 
Djava.util.logging.config.file="%CATALINA_BASE%\conf\logging.properties"

linux下修改cataline.sh:

JAVA_OPTS="-Dcom.sun.management.jmxremote.port=10090 
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false 
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager 
-Djava.util.logging.config.file=%CATALINA_BASE\conf\logging.properties"
注意JDK\jre\lib\management\management.properties文件必须存在。重新启动Tomcat节点,然后用jconsole连接

⑥.Tomcat增加一个应用

在server.xml的Host标签中增加行
<Context displayName="OA" docBase="/app/web-apps/GACWP" path=""/>
path表示上下文名称,空表示根路径
5.你有没有用过Spring的AOP? 是用来干嘛的? 大概会怎么使用?

答:常用的AOP及时安全检验,日志操作,事务操作等。

假如没有aop,在做日志处理的时候,我们会在每个方法中添加日志处理;
但大多数的日志处理代码是相同的,为了实现代码复用,我们可能把日志处理抽离成一个新的方法。
但是这样我们仍然必须手动插入这些方法。
但这样两个方法就是强耦合的,假如此时我们不需要这个功能了,或者想换成其他功能,那么就必须一个个修改。
通过动态代理,可以在指定位置执行对应流程。这样就可以将一些横向的功能抽离出来形成一个独立的模块,然后在指定位置插入这些功能。
这样的思想,被称为面向切面编程,亦即AOP。

为了在指定位置执行这些横向的功能,需要知道指定的是什么地方,把切点和通知合在一起就是切面了,
一个切面指定了在何时何地执行何种方法。在spring aop中如此定义这个切面:
@Aspect
@Component
public class UserAspect {
    @Before("execution(* com.aop.service.impl.UserServiceImpl.login(..))")
    public void loginLog(){
        System.out.println("user login");
    }
}
使用注解@Aspect将某个特定的类声明为切面,这样,该类下的方法就可以声明为横向的功能点后插入到指定位置。
使用execution表达式声明在这个切点,第一个位置指定了方法的返回值,*号代表任意类型的返回值,
然后是所在的类和方法名,*号同样代表任意,就是该类中任意的方法,在上一个例子中方法名是login,
则是指定了该类中的login方法。然后最后一个参数是方法入参,因为java中支持重载,
所以这个参数可以帮助你更精确的进行定位。两点表示任意参数类型。
这样,execution表达式告诉了程序该在何地执行通知。
而被诸如@Before注解修饰的方法就是通知的内容,也就是做什么。

至此,我们就可以使用spring aop,但是还有两点需要得到注意
1.  将切面类声明为一个bean
2.  切点指定的方法所在的类也同样需由spring注入才能生效

6.如果一个接口有2个不同的实现, 那么怎么来Autowire一个指定的实现?
//使用@Qualifier("aaaService")注解
@Service
public class AaaService implements IChangePassword {
    @Override
    public void changePassword(String username, String password) {}
}

@Service
public class BbbService implements IChangePassword {
    @Override
    public void changePassword(String username, String password) {}
}
 
public class AccountController extends BaseController {
    @Autowired
    @Qualifier("aaaService")
    private IChangePassword aaaService;
 
    @Autowired
    @Qualifier("bbbService")
    private IChangePassword bbbService;
}
7.如果想在某个Bean生成并装配完毕后执行自己的逻辑,可以什么方式实现?

答:有时,我们需要在启动bean时初始化bean属性,例如读取perporties文件,对属性进行赋值;启动容器时让某个method方法执行等等。这时需要在进行配置,让bean在注入时启动指定方法。

共有以下几种方法:
①、如果是通过XML配置文件进行Bean的生成,我们可以在配置Bean的时候,使用init-method=“executionMethod”属性,这样在当前Bean实例化完成后,就会自动执行指定的executionMethod。executionMethod为定义在Bean中的一个方法。

<bean id="initializingBean" class="全类名" init-method="executionMethod"></bean>

②、可以让Bean实现InitializationBean接口,并重写其afterPropertiesSet()方法。

③、给需要调用的方法加上@PostConstruct注解,即在构造方法后调用。比如
@PostConstruct
private void initMethod1(){ .....}

7.SpringBoot没有放到web容器里为什么能跑HTTP服务?

答:因为springboot中内嵌了tomcat,jetty,undertow。

8.SpringBoot中如果你想使用自定义的配置文件而不仅仅是application.properties,应该怎么弄?

答:①.在类中使用注解 @PropertySource,例如:@PropertySource("classpath:define.properties")
②.在配置文件中引入配置文件<context:property-placeholder location="classpath:jdbc.properties,classpath:rabbitmq.properties"/>

9.SpringMVC如果希望把输出的Object(例如XXResult或者XXResponse)这种包装为JSON输出, 应该怎么处理?

答:①.加入jackson依赖的jar包:

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.8</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>

②.配置文件中进行配置:

           <bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                        <value>application/json;charset=UTF-8</value>
                </property
            </bean>

③.直接使用注解的方式“@ResponseBody”自动将返回值转化为json格式.

10.如果有很多数据插入MYSQL 你会选择什么方式?

答:在MySQL的命令行界面执行以下命令:LOAD DATA INFILE 'd:/t.sql' INTO TABLE e_tuike_goods FIELDS TERMINATED BY ',';

11.如果查询很慢,你会想到的第一个方式是什么?索引是干嘛的?

答:sql语句优化或者该数据表添加索引,

就比如一本书,你想看第六章第六节讲的是什么,你会怎么做,一般人肯定去看目录,
找到这一节对应的页数,然后翻到这一页。这就是目录索引,帮助读者快速找到想要的章节。
在数据库中,我们也有索引,其目的当然和我们翻书一样,能帮助我们提高查询的效率。
索引就像目录一样,减少了计算机工作量,对于表记录较多的数据库来说是非常实用的,
可以大大的提高查询的速度。否则的话,如果没有索引,计算机会一条一条的扫描,
每一次都要扫描所有的记录,浪费大量的cpu时间。

我们都知道对于一个无序的表,和一个有序的表,有序表的查询方法会有更多地选择,
每种查询方法的效率也不同,其实为表建立索引,也就是对表中的记录按照索引字段排序。
12.查询死掉了,想要找出执行的查询进程用什么命令?

答: ps S 列出程序时,包括已中断的子程序资料。

13.读写分离是怎么做的?你认为中间件会怎么来操作?这样操作跟事务有什么关系?

答:①.读写分离的实现原理就是在执行SQL语句的时候,判断到底是读操作还是写操作,把读的操作转向到读的服务器上(从服务器,一般是多台),写的操作转到写的服务器上(主服务器,一般是一台),当然为了保证多台数据库数据的一致性,需要主从复制。
主从复制的实现原理是:mysql中有一种日志,叫做bin日志(二进制日志),会记录下所有修改过数据库的sql语句。
主从复制的原理实际是多台服务器都开启bin日志,然后主服务器会把执行过的sql语句记录到bin日志中,之后从服务器读取这个bin日志,把该日志的内容保存到自己中继日志里面,从服务器再把中继日志中记录的sql语句同样的执行一遍,这样从服务器上的数据就和主服务器相同了。
②.中间件有淘宝开源的cobar,以及后来开源社区根据cobar进行二次开发的mycat

14.你知道哪些或者你们线上使用什么GC策略? 它有什么优势,适用于什么场景?
使用SerialGC的场景: 
1、如果应用的堆大小在100MB以内。 
2、如果应用在一个单核单线程的服务器上面,并且对应用暂停的时间无需求。 
使用ParallelGC的场景: 
如果需要应用在高峰期有较好的性能,但是对应用停顿时间无高要求(比如:停顿1s甚至更长)。 
使用G1、CMS场景: 
1、对应用的延迟有很高的要求。 
2、如果内存大于6G请使用G1。
15.JAVA类加载器包括几种?它们之间的父子关系是怎么样的?双亲委派机制是什么意思?有什么好处?

答:java类加载器包括:
1.启动类加载器(Bootstrap ClassLoader),也叫跟类加载器,负责加载java的核心类库,例如(%JAVA_HOME%/lib)目录下的rt.jar(包含System,String这样的核心类),跟类加载器非常特殊,它不是java.lang.ClassLoader的子类,它是JVM自身内部由C/C++实现的,并不是java实现的。

2.扩展类加载器(Extension ClassLoader),负责加载扩展目录(%JAVA_HOME%/jre/lib/ext)下的jar包,用户可以把自己开发的类打包成jar包放在这个目录下即可扩展核心类以外的功能

3.系统类加载器(System ClassLoader\APP ClassLoader),又称为应用程序类加载器,是加载CLASSPATH环境变量下所指定的jar包与类路径,一般来说,用户自定义的就是由APP ClassLoader加载的

各类加载器之间的关系:

以结合关系复用父类加载器的父子关系,注意,这里的父子关系并不是继承关系实现的

类加载器的双亲委派加载机制:

当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父类去完成,每一个层次类加载都是如此,因此所有的加载请求都应该传送到启动类加载器中,只有当父类加载器反馈自己无法完成这个请求的时候(在他的加载路径里找不到这个所需要加载的类),子类加载器才会尝试自己去加载。

双亲委派模型的源码实现:

主要体现在ClassLoader的loadClass()方法,思路很简单:先检查是否已经被加载,若没有被加载则调用父类的LoadClass()方法,若父类加载器为空,则默认使用启动类加载器作为父类加载器,如果父类加载器加载失败,抛出ClassNotFoundException异常后,调用自己的findClass()方法进行加载。

16.如何自定义一个类加载器?你使用过哪些或者你在什么场景下需要一个自定义的类加载器吗?堆内存设置的参数是什么?

答:我们需要的类不一定存放在已经设置好的ClassPath下(有系统类加载器APPClassLoader加载的路径),对于自定义路径下的class类文件的加载,我们需要自己的ClassLoader。
有时我们不一定是从类文件中读取类,可能是从网络的输入流中读取类,这就需要做一些加密和解密操作,这就需要自己实现加载类的逻辑,当然其他的特殊处理也同样适用。
可以定义类的实现机制,实现类的热部署,如OSGi中的bundle模块就是通过实现自己的ClassLoader实现的。

public class MyClassLoader extends ClassLoader {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException,SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        if (args.length == 0) {
            System.out.println("没有类啊");
        }
        // 取出第一个参数,就是需要运行的类
        String procressClass = args[0];
        // 剩余参数为运行目标类的参数,将这些参数复制到一个新数组中
        String[] procress = new String[args.length - 1];
        System.arraycopy(args, 1, procress, 0, procress.length);
        MyClassLoader myClassLoader = new MyClassLoader();
        Class<?> class1 = myClassLoader.loadClass(procressClass);
        Method main = class1.getMethod("main", (new 
        String[0]).getClass());
        Object argsArray[] = { procress };
        main.invoke(null, argsArray);
    }

    /**
     * @TODO 读取文件内容
     */
    public byte[] getBytes(String fileName) {
        File file = new File(fileName);
        long len = file.length();
        byte[] raw = new byte[(int) len];
        try {
            FileInputStream fileInputStream =
             new FileInputStream(file);
            try {
                int r = fileInputStream.read(raw);
                fileInputStream.close();
                if (r != len)
                    throw new IOException("fail to read
                     the file...");
            } catch (IOException e) {
                e.printStackTrace();
            }
            return raw;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * @TODO 编译java文件
     */
    public boolean complie(String javaFile) {
        System.out.println("正在编译...");
        Process process = null;
        try {
            process = Runtime.getRuntime().exec("javac " + javaFile);
            try {
                process.waitFor();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        int result = process.exitValue();
        return result == 0;
    }

    /**
     * @TODO 关键,重写findClass方法
     */
    @Override
    protected Class<?> findClass(String arg0) throws ClassNotFoundException {
        Class<?> class1 = null;
        String filePath = arg0.replaceAll(".", "/");
        String className = filePath + ".class";
        String javaName = filePath + ".java";
        File javaFile = new File(javaName);
        File classFile = new File(className);
        if (javaFile.exists()
                && (!classFile.exists() || javaFile.lastModified() > classFile .lastModified())) {
            if (!complie(javaName) || !classFile.exists()) {
                throw new ClassNotFoundException(javaName + " Class找不到");
            }
        }
        if (classFile.exists()) {
            byte[] raw = getBytes(className);
            class1 = defineClass(arg0, raw, 0, raw.length);
        }

        if (class1 == null) {
            throw new ClassNotFoundException(javaName + " 加载失败");
        }
        return class1;
    }
 }
17.HashMap和Hashtable的区别。

答:HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的区别,主要的区别有:线程安全性,同步(synchronization)以及速度。

1.HashMap是非synchronized的,并可以接收null,HashMap可以接受为null的键(key)和值(value),而Hashtable则不行。

2.Hashtable是线程安全的,多个线程是可以共享一个Hashtable,而如果没有正确的同步的话,多个线程是不能共享HashMap的,java5提供了ConcurrentHashMap,它是HashTable的替代,扩展性更好。

3.HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的,所以当有其他线程改变了HashMap的结构,将会抛出ConcurrentModificationException。

4.由于Hashtable是线程安全的,所以在单线程环境下他比HashMap要慢,如果不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。

注意:
1.synchronized意味着在一次仅有一个线程能够更改Hashtable,就是说任何线程要更新Hashtable时要首先获得同步锁,其他线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。

2.使HashMap同步:
Map m = Collections.synchronizeMap(hashMap);

3.仅在需要线程安全的时候使用HashTable,使用java5或以上的话,使用ConcurrentHashMap。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,012评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,628评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,653评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,485评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,574评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,590评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,596评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,340评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,794评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,102评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,276评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,940评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,583评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,201评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,441评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,173评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,136评论 2 352