面试题176-200

176.Java中的ThreadPoolExecutor类

 下面解释下一下构造器中各个参数的含义:

  • corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
  • maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
  • keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
  • unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性:
复制代码
TimeUnit.DAYS;               //
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒
复制代码
  • workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;

  ArrayBlockingQueue和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue和Synchronous。线程池的排队策略与BlockingQueue有关。

  • threadFactory:线程工厂,主要用来创建线程;
  • handler:表示当拒绝处理任务时的策略,有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。 
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务 


177.hibernate的事务级别

3.    多个事务并发引起的问题:
3.1.          第一类丢失更新:撤消一个事务时,把其它事务已提交的更新的数据覆盖了。
3.2.        脏读:一个事务读到另一个事务未提交的更新数据。
3.3.        幻读:一个事务执行两次查询,但第二次查询比第一次查询多出了一些数据行。
3.4.        不可重复读:一个事务两次读同一行数据,可是这两次读到的数据不一样。
3.5.        第二类丢失更新:这是不可重复读中的特例,一个事务覆盖另一个事务已提交的更新数据。
4.    事务隔离级别:
为了解决多个事务并发会引发的问题。数据库系统提供了四种事务隔离级别供用户选择。
o Serializable:串行化。隔离级别最高
o Repeatable Read:可重复读。
o Read Committed:读已提交数据。
o Read Uncommitted:读未提交数据。隔离级别最差。
数据库系统采用不同的锁类型来实现以上四种隔离级别,具体的实现过程对用户是透明的。用户应该关心的是如何选择合适的隔离级别。
对于多数应用程序,可以优先考虑把数据库系统的隔离级别设为Read Committed,它能够避免脏读,而且具有较好的并发性能。
每个数据库连接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别。 JDBC数据库连接使用数据库系统默认的隔离级别。在Hibernate的配置文件中可以显示地设置隔离级别。每一种隔离级别对应着一个正整数。
Read Uncommitted: 1
Read Committed: 2
Repeatable Read: 4
Serializable: 8
在hibernate.cfg.xml中设置隔离级别如下:
    <session-factory>
<!-- 设置JDBC的隔离级别 -->
<property name="hibernate.connection.isolation">2</property>
</session-factory>
设置之后,在开始一个事务之前,Hibernate将为从连接池中获得的JDBC连接设置级别。需要注意的是,在受管理环境中, 如果Hibernate使用的数据库连接来自于应用服务器提供的数据源,Hibernate不会改变这些连接的事务隔离级别。在这种情况下,应该通过修改应用服务器的数据源配置来修改隔离级别。
5.    并发控制:
当数据库系统采用Red Committed隔离级别时,会导致不可重复读和第二类丢失更新的并发问题,在可能出现这种问题的场合。可以 在应用程序中采用悲观锁或乐观锁来避免这类问题。
5.1.       乐观锁(Optimistic Locking)
乐观锁假定当前事务操纵数据资源时,不会有其他事务同时访问该数据资源,因此不作数据库层次上的锁定。为了维护正确的数据,乐观锁使用应用程序上的版本控制(由程序逻辑来实现的)来避免可能出现的并发问题。
唯一能够同时保持高并发和高可伸缩性的方法就是使用带版本化的乐观并发控制。版本检查使用版本号、 或者时间戳来检测更新冲突(并且防止更新丢失)。
5.1.1.       使用版本检查(<version>):
Hibernate中通过版本号检查来实现后更新为主,这也是Hibernate推荐的方式。在数据库表中加入一个version(版本)字段,在读取数据时连同版本号一起读取,并在更新数据时比较版本号与数据库表中的版本号,如果等于数据库表中的版本号则予以更新,并递增版本号,如果小于数据库表中的版本号就抛出异常。
使用<version>进行版本控制的步骤:
1)       在持久化类中定义一个代表版本号的属性:
package org.qiujy.domain.versionchecking;
 
import java.util.Date;
 
public class Product implements java.io.Serializable{
       private Long id ;
       /** 版本号 */
       private int version;
       private String name; //产品名
       private String description; //描述--简介
       private Double unitCost; //单价
       private Date pubTime; //生产日期
      
       public Product(){}
 
       //以下为getter()和setter()方法
}
 
2)       在Product.hbm.xml文件中用<version>元素来建立Product类的version属性与表中version字段的映射。
3)       Hibernate在其数据库访问引擎中内置了乐观锁定实现,默认也是选择version方式作为Hibernate乐观锁定实现机制。所以,在配置文件及程序中可以不作其它设置。按往常一样写操作代码。
package org.qiujy.domain.versionchecking;
 
import java.util.Date;
 
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.qiujy.common.HibernateSessionFactory;
 
public class TestVersionChecking {
      
       public static void main(String[] args) {
              Product prod = new Product();
              prod.setName("IBM thinkPad T60");
              prod.setUnitCost(new Double(26000.00));
              prod.setDescription("笔记本电脑");
              prod.setPubTime(new Date());
             
              //test start.......
              Session session = HibernateSessionFactory.getSession();
              Transaction tx =null;
              try{
                     tx = session.beginTransaction();
                    
                     session.save(prod);
                
                    tx.commit();
              }catch(HibernateException e){
                     if(tx != null){
                            tx.rollback();
                     }
                     e.printStackTrace();
              }finally{
                     HibernateSessionFactory.closeSession();
              }
       
//进行更新 测试..
              prod.setDescription("新款的");
             
              Session session2 = HibernateSessionFactory.getSession();
              Transaction tx2 =null;
              try{
                     tx2 = session2.beginTransaction();
                    
                     session2.update(prod);
               
                    tx2.commit();
              }catch(HibernateException e){
                     if(tx2 != null){
                            tx2.rollback();
                     }
                     e.printStackTrace();
              }finally{
                     HibernateSessionFactory.closeSession();
              }
       }
}
新增数据时产生的SQL是:
insert into products (version, name, description, unitCost, pubTime)
    values(?, ?, ?, ?, ?)
程序无需为Product对象的version属性显示赋值,当持久化一个Product对象时,Hibernate会自动为它赋初始值为0。
更新数据时产生的SQL是:
    update
        products
    set
        version=?,
        name=?,
        description=?,
        unitCost=?,
        pubTime=?
    where
        id=?
        and version=?
当Hibernate更新一个Product对象,会根据它的id和version属性到相应的数据库表中定位匹配的记录,如果存在这条匹配的记录,就更新记录,并且把version字段的值加1。若找不到匹配的记录,此时Hibernate会抛出StaleObjectStateException。
 
需要注意的是,由于乐观锁定是使用系统中的程序来控制,而不是使用数据库中的锁定机制,因而如果有人故意自行更新版本信息来超过检查,则锁定机制就无效。所以建议把持久化类中的version的get方法设置为private的。
5.1.2.       使用时间戳(<timestamp>):
跟版本检查的用法相似。不再多说。
5.2.       悲观锁(Pessimistic Locking)
悲观锁假定当前事务操纵数据资源时,肯定还会有其他事务同时访问该数据资源,为了避免当前事务的操作受到干扰,先锁定资源。尽管悲观锁能够防止丢失更新和不可重复读这类并发问题,但是它影响并发性能,因此应该很谨慎地使用悲观锁。

178.Linux系统部署项目常用命令
 linux系统下部署项目的一些常用命令,记录备用
   1、进入Tomcat下的bin目录
  cd  /data/tomcat/bin
   2、使用Tomcat停止命令
  ./shutdown.sh
   3、查看正在运行的Tomcat进程,tomcat是否以关闭
  ps -ef|grep  java
  如果显示以下相似信息,说明Tomcat还没有关闭
  root      91051     1  0 Apr19 ?        00:30:13 /data/java/bin/java -Djava.util.logging.config.file=/data/tomcat/conf/logging.properties -Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1024m -Xmx1024m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+DisableExplicitGC -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/data/tomcat/endorsed -classpath /data/tomcat/bin/bootstrap.jar -Dcatalina.base=/data/tomcat -Dcatalina.home=/data/tomcat -Djava.io.tmpdir=/data/tomcat/temp org.apache.catalina.startup.Bootstrap start
   4、如果你想直接停掉Tomcat,你可以使用kill命令,直接杀死Tomcat进程
  kill  91051
  kill -9 91051
  下面的这个更强一些,使用命令之后可以使用第三步的命令查看一下是否停掉tomcat,如果出现下面的输出说明tomcat已停掉了
  root      91051     1  0 Apr19 ?        00:30:30 [java] <defunct>
   5、最后,启动Tomcat
  ./startup.sh
   6、启动后查看日志
  cd  /data/tomcat/logs
  tail -f catalina.out


179.comparator接口与Comparable接口的区别

180.jvm默认的垃圾回收器

新生代串行收集器:(默认收集器)

  算法:复制算法

  -XX:+UseSerialGC 指定使用新生代串行收集器和老年代串行收集器

  优点:效率高,久经考验

  缺点:串行,如果回收对象过多,或者堆过大,停顿时间会过长。

190.吞吐量计算

吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)


191.jvm 垃圾回收器重要知识

http://blog.csdn.net/lzwglory/article/details/50157243


192.吞吐量和并发数的关系




193.redis都支持哪些存储结构?

String——字符串
Hash——字典
List——列表
Set——集合
Sorted Set——有序集合

 详细内容:  http://blog.csdn.net/a137268431/article/details/50717690


192.mysql 分区的4种方式:

Range(范围)–这种模式允许将数据划分不同范围。例如可以将一个表通过年份划分成若干个分区。

create table range( 
  id int(11), 
  money int(11) unsigned not null, 
  date datetime 
  )partition by range(year(date))( 
  partition p2007 values less than (2008), 
  partition p2008 values less than (2009), 
  partition p2009 values less than (2010) 
  partition p2010 values less than maxvalue 
);


Hash(哈希)–这中模式允许通过对表的一个或多个列的Hash Key进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区。例如可以建立一个对表主键进行分区的表。

create table hash( 
  a int(11), 
  b datetime 
  )partition by hash (YEAR(b) 
  partitions 4;

Key(键值)-上面Hash模式的一种延伸,这里的Hash Key是MySQL系统产生的。

create table t_key( 
  a int(11), 
  b datetime) 
  partition by key (b) 
  partitions 4;


List(预定义列表)–这种模式允许系统通过预定义的列表的值来对数据进行分割。

create table list( 
  a int(11), 
  b int(11) 
  )(partition by list (b) 
  partition p0 values in (1,3,5,7,9), 
  partition p1 values in (2,4,6,8,0) 
 );

193.分区与分表作用

为什么要分表和分区?

日常开发中我们经常会遇到大表的情况,所谓的大表是指存储了百万级乃至千万级条记录的表。这样的表过于庞大,导致数据库在查询和插入的时候耗时太长,性能低下,如果涉及联合查询的情况,性能会更加糟糕。分表和表分区的目的就是减少数据库的负担,提高数据库的效率,通常点来讲就是提高表的增删改查效率。

什么是分表?

分表是将一个大表按照一定的规则分解成多张具有独立存储空间的实体表,我们可以称为子表,每个表都对应三个文件,MYD数据文件,.MYI索引文件,.frm表结构文件。这些子表可以分布在同一块磁盘上,也可以在不同的机器上。app读写的时候根据事先定义好的规则得到对应的子表名,然后去操作它。

什么是分区?

分区和分表相似,都是按照规则分解表。不同在于分表将大表分解为若干个独立的实体表,而分区是将数据分段划分在多个位置存放,可以是同一块磁盘也可以在不同的机器。分区后,表面上还是一张表,但数据散列到多个位置了。app读写的时候操作的还是大表名字,db自动去组织分区的数据。

mysql分表和分区有什么联系呢?
1.都能提高mysql的性高,在高并发状态下都有一个良好的表现。
2.分表和分区不矛盾,可以相互配合的,对于那些大访问量,并且表数据比较多的表,我们可以采取分表和分区结合的方式(如果merge这种分表方式,不能和分区配合的话,可以用其他的分表试),访问量不大,但是表数据很多的表,我们可以采取分区的方式等。
3.分表技术是比较麻烦的,需要手动去创建子表,app服务端读写时候需要计算子表名。采用merge好一些,但也要创建子表和配置子表间的union关系。
4.表分区相对于分表,操作方便,不需要创建子表。

详细资料参考:http://blog.csdn.net/a137268431/article/details/50725406


194.三种java实现多线程的方式

  1. 继承Thread类,重写父类run()方法
  2. 实现runnable接口
  3. 使用ExecutorService、Callable、Future实现有返回结果的多线程(JDK5.0以后)

详解:http://www.jb51.net/article/70534.htm


195.MySQL数据库4种事务隔离级别:

  1. READ-UNCOMMITTED(读取未提交内容)级别:可以看出READ-UNCOMMITTED隔离级别,当两个事务同时进行时,即使事务没有提交,所做的修改也会对事务内的查询做出影响,这种级别显然很不安全。但是在表对某行进行修改时,会对该行加上行共享锁,会产生脏读。
  2. READ-COMMITTED(读取提交内容):READ-COMMITTED事务隔离级别,只有在事务提交后,才会对另一个事务产生影响,并且在对表进行修改时,会对表数据行加上行共享锁,会出不可重复读
  3. REPEATABLE-READ(可重读)mysql默认级别:REPEATABLE-READ事务隔离级别,当两个事务同时进行时,其中一个事务修改数据对另一个事务不会造成影响,即使修改的事务已经提交也不会对另一个事务造成影响。
     在事务中对某条记录修改,会对记录加上行共享锁,直到事务结束才会释放,会出现幻读。
  4. SERIERLIZED(可串行化):SERIALIZABLE事务隔离级别最严厉,在进行查询时就会对表或行加上共享锁,其他事务对该表将只能进行读操作,而不能进行写操作。


196.共享锁与排它锁的区别:

共享锁:由读表操作加上的锁,加锁后其他用户只能获取该表或行的共享锁,不能获取排它锁,也就是说只能读不能写

排它锁:由写表操作加上的锁,加锁后其他用户不能获取该表或行的任何锁,典型是mysql事务中

start transaction;

select * from user where userId = 1 for update;

执行完这句以后

  1)当其他事务想要获取共享锁,比如事务隔离级别为SERIALIZABLE的事务,执行

  select * from user;

   将会被挂起,因为SERIALIZABLE的select语句需要获取共享锁

  2)当其他事务执行

  select * from user where userId = 1 for update;

  update user set userAge = 100 where userId = 1; 

  也会被挂起,因为for update会获取这一行数据的排它锁,需要等到前一个事务释放该排它锁才可以继续进行


详细参考:http://www.cnblogs.com/zemliu/archive/2012/06/17/2552301.html

197.脏读,不可重复读,幻读的区别?

脏读(Dirty Read)

脏读意味着一个事务读取了另一个事务未提交的数据,而这个数据是有可能回滚

不可重复读(Unrepeatable Read)

不可重复读意味着,在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。
例如:事务B中对某个查询执行两次,当第一次执行完时,事务A对其数据进行了修改。事务B中再次查询时,数据发生了改变

幻读(phantom read)

幻读,是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样.


198.Java两个变量的互换(不借助第3个变量)具体实现方法

import java.util.Scanner;
public class VariableExchange {
    public static void main(String[] args){
        System.out.println("请输入变量A的值");
        Scanner scanner=new Scanner(System.in);
        long A=scanner.nextLong();
        System.out.println("请输入变量B的值");
        Scanner scannerB=new Scanner(System.in);
        long B=scannerB.nextLong();
        System.out.println("A="+A+"\t"+"B="+B);
        System.out.println("执行变量互换...");
        A=A^B;
        B=B^A;
        A=A^B;
        System.out.println("A="+A+"\t"+"B="+B);
    }
}


199.请手动证明多边形内角和是360°

如图是一个任意n多边形,将各边延长得到n个平角,角度之和为180n。
这些平角由多边形的所有内角和外角组成,因为多边形内角和等于(n-2)*180,所以外角和就是180n-(n-2)*180=360°。证毕。




200.3种线程安全的Map与3种线程安全的List

  1. Hashtable、Collections.synchronizedMap、ConcurrentHashMap 
  2. Vector、Collections.synchronizedList、CopyOnWriteArrayLis
同步的集合类Hashtable 和 Vector,以及同步的包装器类 Collections.synchronizedMap 和 Collections.synchronizedList,为Map 和 List提供了基本的有条件的线程安全的实现。然而,某些因素使得它们并不适用于具有高度并发性的应用程序中——它们的 集合范围的单锁特性对于可伸缩性来说是一个障碍,而且,很多时候还必须在一段较长的时间内锁定一个集合,以防止出现ConcurrentModificationExceptions异常。 ConcurrentHashMap 和 CopyOnWriteArrayList实现提供了更高的并发性,同时还保住了线程安全性,只不过在对其调用者的承诺上打了点折扣。ConcurrentHashMap 和 CopyOnWriteArrayList并不是在您使用HashMap 或 ArrayList的任何地方都一定有用,但是它们是设计用来优化某些特定的公用解决方案的。许多并发应用程序将从对它们的使用中获得好处。





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值