文章目录
- 面向对象和面向过程的区别
- java的四个基本特性
- 重载和重写的区别
- 构造函数
- 访问控制符的区别
- Static关键字
- final关键字
- String,StringBuffer,StringBuilder的区别
- equals()和==的区别
- hashCode和equals的关系
- 抽象类和接口的区别
- 自动装箱和拆箱
- 什么是泛型,为什么使用,什么是类型擦除
- java中集合类的关系
- HashMap实现原理
- HashTable实现原理
- HashMap和HashTable的区别
- HashSet 与 TreeSet 的比较
- ArrayList和Vector区别
- ArrayList和LinkedList的区别
- Collection和Collections的区别
- ConcurrenthashMap和hashtable的区别
- Error、Exception异同
- checked和unchecked异常
- java如何实现代理机制
- 多线程的实现方式
- java线程
- volatile关键字
- synchronized关键字
- 多线程如何进行信息交互
- sleep和wait的区别
- 产生死锁的必要条件
- 死锁的预防
- 锁优化
- 守护进程是什么,如何实现
- java线程池技术及原理
- java并发包oncurrent及常用类
- JAVA中的NIO,BIO,AIO分别是什么
- IO和NIO区别
- 序列化和反序列化
- Java内存模型
- javaGC机制
- 类的加载机制和双亲委派机制
- copyOnRight
面向对象和面向过程的区别
- 面向过程优点:性能高,因为类调用的时候需要实例化,开销比较大;
- 面向对象优点:易维护、易复用、易扩展,因为面向对象有封装、继承、多态的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。
java的四个基本特性
抽象
- 把现实中的某一类东西用代码表示,通常叫做类或者接口。
- 抽象包括两个方面:数据抽象–对象的域,过程抽象–对象的方法。
封装:
- 把客观事物抽象成类,类的域和方法只让可信的类或者对象操作,对不可信的隐藏。
- 封装分为域的封装和方法的封装。
继承:
- 把拥有共同特性的多个类抽象成一个类,这个类是他们的父类,这些类继承这个父类。
- 父类的意义在于抽取多类事物的共性。
多态:
- 允许不同类的对象对同一消息(方法调用)做出响应。即同一消息可以根据发送对象的不同而采取不同的行为方式。
- 实现技术:动态绑定–根据实例的运行期类型调用相应的方法。
- 作用:消除类型之间的耦合。
- 必要条件:继承、重写、父类引用指向子类的对象。
重载和重写的区别
- 重载:在同一个类中,方法名相同,参数列表不同,返回值和访问修饰符可以不同也可以相同,发生在编译时。
- 重写:在父子类中,方法名和参数列表相同。
类型范围:子类返回值类型<=父类返回值类型(不包括基本类型),子类抛出异常<=父类抛出异常,子类访问修饰符>=父类访问修饰符(如果父类方法的访问修饰符为private,则子类中就不是重写)。
构造函数
- Constructor不能被override,不能用static修饰。
访问控制符的区别
- public:任何类。
- protected:本类、同包的类、子类。
- default:本类、同包的类。
- private:本类。
Static关键字
- static主要有4中使用情况:成员变量、成员方法、初始化块和内部类。
- 被修饰的属于类,而不是属于对象。
final关键字
- 修饰类,那么这个类不能被继承。
- 修饰方法,那么这个方法不能被override。
- 修饰属性,那么这个属性值不能被修改。
- 在匿名类中所有变量都必须是final变量。
- final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。
- 对于集合对象声明为final指的是引用不能被更改,但是你可以向其中增加,删除或者改变内容。
String,StringBuffer,StringBuilder的区别
可变性
- String中使用final char value[],不可变。
- StringBuffer,StringBuilder继承AbstractStringBuilder使用char[] value,可变。
线程安全性
- String不可变,线程安全。
- StringBuffer对方法使用同步锁,线程安全。
- StringBuilder没有对方法加同步锁,非线程安全。
性能
- String每次改变时都会生成一个新的String对象。
- StringBuffer,StringBuilder只是对象本身操作,StringBuilder性能比StringBuilder高10%。
equals()和==的区别
- equals()是方法,==是操作符。
- 使用==比较原生类型如:boolean、int、char等等,使用equals()比较对象。
- ==返回是否两个引用指向相同的对象,equals()的返回结果依赖于具体业务实现。
- 字符串的对比使用equals()代替==操作符。
hashCode和equals的关系
- equals相等–>hashcode相等。
- 重写equals时一定要重写hashcode,否则在使用hashtable等需要使用hashcode的时候很有可能会出问题(contain函数在hashcode相同的bucket中查找是否存在equals的对象)。
- 使用HashMap,如果key是自定义的类,就必须重写hashcode()和equals()。
- http://blog.youkuaiyun.com/tuolaji8/article/details/48417031
如何重写
//user类
public class User{
private String name;
private int age;
private String passport;
//getters and setters, constructor
}
User user1 = new User("mkyong", 35, "111222333");
User user2 = new User("mkyong", 35, "111222333");
//比较结果
System.out.println(user1.equals(user2)); // false
//经典方式
public class User {
private String name;
private int age;
private String passport;
//getters and setters, constructor
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof User)) {
return false;
}
User user = (User) o;
return user.name.equals(name) &&
user.age == age &&
user.passport.equals(passport);
}
//Idea from effective Java : Item 9
@Override
public int hashCode() {
int result = 17;
result = 31 * result + name.hashCode();
result = 31 * result + age;
result = 31 * result + passport.hashCode();
return result;
}
}
//对于JDK7及更新版本,你可以是使用java.util.Objects 来重写 equals 和 hashCode 方法,代码如下
import java.util.Objects;
public class User {
private String name;
private int age;
private String passport;
//getters and setters, constructor
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof User)) {
return false;
}
User user = (User) o;
return age == user.age &&
Objects.equals(name, user.name) &&
Objects.equals(passport, user.passport);
}
@Override
public int hashCode() {
return Objects.hash(name, age, passport);
}
}
抽象类和接口的区别
设计理念
- abstarct class体现继承关系,父类和子类在概念本质上相同。
- interface的实现者仅仅实现interface定义的契约。
语法
-
语言
abstract class A { abstract void method(); } interface A { void method(); }
-
一个类只能继承一个abstract class,但可以实现多个interface。
-
interface只能够有public static final的域(interface中一般不定义域),所有方法都是public abstract的。
-
abstract class可以有非static域和非static方法。
自动装箱和拆箱
- 装箱:将基本类型用对应的包装类包类型。
- 拆箱:将包装类型转换为基本数据类型。
- java编译器会在编译器根据语法决定是否装箱和拆箱。
什么是泛型,为什么使用,什么是类型擦除
- 泛型:参数化类型,适用于多种类型。
- 使用:创建集合时就指定元素的类型,该集合只能保存制定类型的元素。
- 类型擦除:java编译器生成的字节码不包含泛型类型信息,类型信息在编译处理时被擦除,用最顶级的父类类型替换。
java中集合类的关系
- List,Set,Queue接口继承自Collection接口。
- Set无序、元素不重复,主要实现类有HashSet(底层用hashmap实现,基于哈希表)和TreeSet(红黑树)
- List有序、元素可重复,主要实现类有ArrayList,LinkedList,Vector。
- Map和Collection接口无关,Map是key对value的映射集合,key不能重复,value可以重复,主要实现类有HashMap,TreeMap,HashTable。
HashMap实现原理
HashTable实现原理
HashMap和HashTable的区别
- 线程安全:HashTable线程安全,HashMap非线程安全。
- key值:HashTable不允许有null(key和value),HashMap允许null(key和value)。
- 方法:HashTable多出一个contains方法,与containsValue功能一样,而不是containsKey。
- 迭代器:HashTable用Enumeration遍历;HashMap用Iterator遍历。
- 数组大小:HashTable中数组默认大小11,增加方式old*2+1;HashMap中数组默认大小16,增加方式<<1。
- hashcode:HashTable直接使用对象的hashCode;HashMap重新计算hash值,并且用与代替求模。
相关
- Enumeration:只能读取不能修改,不支持fail-fast机制。
- Iterator:能读取能删除,支持fail-fast机制。
- fail-fast机制:当线程A通过iterator遍历集合时,若集合的内容被其他线程改变,那么线程A就会抛出异常,即产生fail-fast事件。
- http://blog.youkuaiyun.com/caihaijiang/article/details/6280251
- http://alex09.iteye.com/blog/539549
HashSet 与 TreeSet 的比较
- HashSet是基于 HashMap 实现的,放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存,而 HashMap 的 value 则存储了一个 PRESENT,它是一个静态的 Object 对象。
- TreeSet是依靠TreeMap来实现的,TreeSet是一个有序集合,TreeSet中的元素将按照升序排列,缺省是按照自然排序进行排列,意味着TreeSet中的元素要实现Comparable接口。或者有一个自定义的比较器。
ArrayList和Vector区别
- Vector线程安全,ArrayList非线程安全。
- 初始容量都是10,自动扩容时,Vector增长1倍,ArrayList增长1/2。
ArrayList和LinkedList的区别
区别
- ArrayList底层数组实现,可以随机查找。
- LinkedList底层双向链表实现,增删速度快。实现Queue接口。
场景
- LinkedList适合从中间插入或删除。
- ArrayList适合检索和在末尾插入或删除。
Collection和Collections的区别
- Collection是一个接口,提供对集合对象基本操作的通用接口方法。
- Collections是一个类,包含集合操作的静态方法。该类不能实例化(因为它的构造函数被private修饰)。
- Collections.sort(List list);
ConcurrenthashMap和hashtable的区别
- Hashtable的synchronized是针对整张表,Concurrenthashmap使用多个锁控制对表不同部分的修改。
- Java集合—ConcurrentHashMap原理分析
- ConcurrentHashMap
Error、Exception异同
- Error和Exception都继承tThrowable。
- Error类一般指与虚拟机相关的问题,比如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢出。这类错误导致的应用程序中断仅靠程序本身无法恢复和预防,应该终止程序。
- Exception类表示程序可以处理的异常,可以捕获并且可能恢复。这类异常应该尽可能处理。
checked和unchecked异常
unchecked Exception
- 通常是自身的问题。
- 程序瑕疵或者逻辑错误,运行时无法恢复。
- 包括Error和RuntimeException及其子类。(比如:NullPointerException,没有人特意去处理)
- 语法上不需要抛出异常。
checked Exception
- 程序不能直接控制的无效外界情况(用户输入,数据库问题,网络异常,文件丢失)。
- 除了Error和RuntimeException及其子类之外的类。
- 需要try catch处理或者throws抛出异常。
java如何实现代理机制
- 代理模式:为其他对象提供一种代理,以控制对这个对象的访问
- jdk和cglib动态代理
多线程的实现方式
-
Thread + Runnable。(thread.start())
-
ExecutorService + Runable,不返回值。
-
ExecutorService + Callable,返回Future。
public class ThreadEx { public static void main(String[] args) throws InterruptedException, ExecutionException { Thread thread = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }); thread.start(); ExecutorService executorService = Executors.newCachedThreadPool(); executorService.execute(new Runnable() { @Override public void run() { // TODO Auto-generated method stub } }); //submit方法有返回值,即call()方法的返回值 Future<Integer> future = executorService.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { // TODO Auto-generated method stub return 1; } }); System.out.println(future.get()); executorService.shutdown(); } }
runnable和callable的区别
- Callable规定的方法是call(),而Runnable规定的方法是run().
- Callable的任务执行后可返回值,而Runnable的任务是不能返回值的
- call()方法可抛出异常,而run()方法是不能抛出异常的。
- 运行Callable任务可拿到一个Future对象,Future表示异步计算的结果。
java线程
6种线程状态
- 新建:new Thread()。
- 运行:Thread.start()。
- 运行中:获得CPU时间后执行run()。
- 就绪:用完cpu时间片、yield。
- 终止:run正常退出;未捕获异常终止run。
- 阻塞:阻塞在等待monitor锁,synchronized
- 等待:等待另一个线程执行某个动作,wait,park
- 超时等待。wait,parkNanos
- ref
- 源码定义
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
线程阻塞的原因
- 线程执行了Thread.sleep(int millsecond)方法,当前线程放弃CP
- U,睡眠一段时间,然后再恢复执行。
- 线程执行了一个对象的wait()方法,直接进入阻塞状态,等待其他线程执行notify()或者notifyAll()方法。
- 线程执行一段同步代码,但是尚且无法获得相关的同步锁,只能进入阻塞状态,等到获取了同步锁,才能回复执行。
- 线程执行某些IO操作,因为等待相关的资源而进入了阻塞状态。比如说监听system.in,但是尚且没有收到键盘的输入,则进入阻塞状态。
什么是线程安全
- 多个线程可能会同时运行一段代码,每次运行的结果和单线程运行的结果是一样的,而且其他变量的值也和预期的是一样的。
- 《Java Concurrency In Practice》:当多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者调用方法进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象就是线程安全的。
如何保证线程安全
- 对非安全的代码进行加锁,synchronized,volatile,lock等。
- 使用线程安全的类。
- 多线程并发情况下,线程共享的变量改为方法级的局部变量。
volatile关键字
- volatile不具有原子性:允许超过一个线程访问该数据。(原子性:就是某系列的操作步骤要么全部执行,要么都不执行,例如:i++分为三个步骤执行,所以仅靠volatile不能保证线程的安全性)
- 要使volatile提供理想的线程安全,必须同时满足两个条件:
- 对变量的写操作不依赖于当前值;
- 变量没有包含在具有其他变量的不变式中。
- 要使volatile提供理想的线程安全,必须同时满足两个条件:
- volatile具有可见性:修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
- volatile具有有序性,禁止指令重排序优化,保证多线程场景的正确性。
可见性
- volatile主要用在多个线程感知实例变量被更改了场合,从而使得各个线程获得最新的值。它使得高速缓存无效(本地内存),强制线程每从主内存中读到volatile修饰的变量,而不是从线程的私有内存中读取变量。修改变量之后写入主内存。从而保证了数据的可见性。
- 每个线程都有一个自己的本地内存空间。线程执行时,先把变量从主内存读取到线程自己的本地内存空间,然后再对该变量进行操作(volatile强制线程从主内存中取 volatile修饰的变量)。
- 对该变量操作完后刷新回主内存。
- java之用volatile和不用volatile的区别
禁用指令重排
- 深入分析volatile的实现原理
- 例子:懒汉式单例。
- 禁用了
singleton = new Singleton()
的指令重排。 - 命令分为三步:1.分配内存;2.初始化对象;3.singleton引用赋值。
- 如果之上的2,3步重排,singleton引用非空,多线程场景可能拿到未初始化的对象。
- 禁用了
public class Singleton {
//volatile 防止指令重排
private static volatile Singleton singleton;
private Singleton() {
}
public static Singleton getInstance() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
//防止指令重排
singleton = new Singleton();
}
}
}
return singleton;
}
}
synchronized关键字
使用方式
- 同步方法和对象
- 静态方法或对象,需要获得类锁。
- 非静态方法或对象,需要获得对象锁。
- 同步代码段
- synchronized(类.class){},需要获得类锁。
- synchronized(this,其他对象),需要获得对象锁
实现原理
- 偏向锁、轻量级锁:cas对象头Markword中的锁标记
- 重量级锁:monitor对象,底层是操作系统互斥锁。
- 对象头指向monitor对象
- monitor被某个线程持有后,处于锁定状态,owner指向持有线程
- 两个队列存放等待获取锁的线程
锁升级
- Jdk1.6后为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁。
- 优先偏向锁。偏向第一个线程,持有偏向锁的线程将不需要进行同步操作。
- 失败升级为CAS轻量级锁
- 多次重试后升级为重量级锁
- 锁膨胀过程
synchronized和Lock的区别
- Lock能完成synchronized所实现的所有功能。
- Lock的锁定是通过AQS代码实现的,synchronized是在JVM层次上实现的
- Lock需要手动在finally从句中释放锁,synchronized自动释放锁。
- Lock可以通过tryLock方法用非阻塞方式去拿锁。
- Lock锁的范围:代码块;synchronized锁的范围:代码块,对象,类。
volatile 与 synchronized 的比较
- volatile轻量级,只能修饰变量。synchronized重量级,还可修饰方法
- volatile只能保证数据的可见性,不能保证原子性,因为多个线程并发访问volatile修饰的变量不会阻塞。
- synchronized不仅保证可见性,而且还保证原子性,因为,只有获得了锁的线程才能进入临界区,从而保证临界区中的所有语句都全部执行。多个线程争抢synchronized锁对象时,会出现阻塞。
多线程如何进行信息交互
- wait(long timeout):挂起线程,释放对象锁,直到时间到期或其他线程调用该对象的notify(),notifyAll()。
- wait():挂起线程,释放对象锁,直到其他线程调用该对象的notify(),notifyAll()。
- notify():唤醒该对象上wait的单个线程。
- notifyAll():唤醒该对象上wait的所有线程。
sleep和wait的区别
类型
- sleep是Thread中的静态方法。
- wait是Object中的实例方法。
锁机制
- sleep:在Synchronized块中调用,线程不会释放对象锁。
- wait:在Synchronized块中调用(必须在同步块,否则抛异常),线程释放对象锁。
- sleep和wait的区别。
产生死锁的必要条件
- 互斥条件:进程使用的资源中至少有一个不能共享。
- 请求与保持条件:进程因为请求资源阻塞时,保持已获得的资源。
- 不剥夺条件:进程已获得的资源在未使用完之前不能被强行剥夺。
- 循环等待条件:A等待B的资源,B等待C的资源,直到某个进程等待A的资源,使得所有进程都无法继续。
死锁的预防
打破产生死锁的四个必要条件中的一个或几个
- 互斥条件:允许进程同时访问某些资源,但是有的资源不允许同时访问,比如打印机。该方法无实用价值。
- 请求与保持条件:实行资源预分配策略,即进程在运行前一次性的向系统申请所需要的全部资源。如果某个资源得不到满足,则不分配资源,进程阻塞;只有满足进程的全部资源需求时,才一次性的将所申请的资源全部分配给该进程。
- 不剥夺条件:允许进程强行从占有者那里夺取资源,即一个进程拥有某些资源,但是新申请的资源不能立刻满足,该进程必须释放所占有的全部资源。该方法实现困难,会降低性能。
- 循环等待条件:实行资源有序分配策略,即把资源类型事先编号,所有进程请求资源先编号小的再编号大的,这样就不会产生环路。
系统安全状态检查
锁优化
自旋锁
- 共享数据的锁定如果只持续很短的时间,挂起和恢复线程不值得。
- 让等待的线程执行一个或者多个忙循环(自旋)。
- 自旋时间可以根据前一次在同一个锁上的自旋时间和锁拥有者的状态决定。
- 如果对于同一个锁对象,自旋等待刚刚获成功获得过锁,并且持有锁的线程正在运行中。虚拟机认为这次自旋也很有可能成功,允许自旋等待更长的时间。
- 如果对于某个锁,自旋很少获得成功,以后获取锁可以省略掉自旋过程,避免浪费CPU。
锁消除
- 虚拟机即时编译器运行时,对一些代码上要求同步,但是检测到不可能存在共享数据竞争的锁进行消除。
锁粗化
- 某些对象频繁的加锁和解锁,例如循环中,这会导致不必要的性能耗损。
- 虚拟器会将加锁同步扩展到整个操作序列之外。
轻量级锁
- 传统的重量级加锁性能消耗大,在基本无竞争的环境中可以使用对象头中的标志位(互斥量)加锁。
- 如果有两个以上的线程争用一个锁,轻量级锁不再有效,膨胀为重量级锁。
偏向锁
- 传统的重量级加锁性能消耗大,在基本无竞争的环境中可以把整个同步取消,偏向第一个取得锁的线程.
- 第一个取得锁的线程获得锁之后不需要同步,当另一个线程尝试获得锁时,偏向模式结束,恢复到未锁定或者轻量级锁状态。
守护进程是什么,如何实现
- 程序运行时在后台提供一种通用服务的线程,当非后台线程结束时,程序也就终止,同时杀死进程中的所有后台线程。
- setDaemon(true)。
java线程池技术及原理
原因
- 如果并发的线程数量很多,并且每个线程执行一个时间很短的任务就结束,这样频繁创建线程会大大降低系统的效率。
- 使用线程池可以使得线程复用,即线程执行完一个任务不被销毁而是继续执行其他任务。
底层实现
- ThreadPoolExecutor类。
任务处理策略
- 线程池中的当前线程数poolSize。
- poolSize<corePoolSize,则每来一个任务,就创建一个线程执行这个任务。
- poolSize>=corePoolSize,则每来一个任务,则添加到任务缓存队列。添加成功(一般队列未满),则该任务会等待空闲线程将其取出执行;添加失败(一般是缓存队列已满)且poolSize<maximumPoolSize,则创建新的线程执行这个任务。
- poolSize>=maximumPoolSize,则采取任务拒绝策略。
- 线程的存活时间:poolSize>corePoolSize,如果某线程空闲时间超过keepAliveTime,线程将被终止,直到poolSize<=corePoolSize;如果允许设置核心池中的存活时间,核心池中的线程空闲时间超过keepAliveTime,该线程也会被终止。
- Java并发编程:线程池的使用
java的四种线程池
- newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。 - newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。 - newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,
那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。 - newScheduledThreadPool
创建一个大小无限的线程池,支持定时以及周期性执行任务的需求。 - 参考
http://blog.youkuaiyun.com/u011974987/article/details/51027795
http://blog.youkuaiyun.com/linghu_java/article/details/17123057
java并发包oncurrent及常用类
JAVA中的NIO,BIO,AIO分别是什么
几个概念
- 同步:使用同步IO时,java自己处理IO读写。
- 异步:使用异步IO时,java将IO读写委托给OS处理,需要将缓冲区地址和大小传给OS,OS需要支持异步IO操作API。
- 阻塞:使用阻塞IO时,java调用会一直阻塞到读写完成后才返回。
- 非阻塞:使用非阻塞IO时,当IO事件分发器通知可读写时继续读写,不可读写时进行其他操作,不断循环直到读写完成。
BIO
- 同步阻塞。
- 服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,可以通过线程池机制改善。
- 适合链接数目较小且固定的架构。
NIO
- 同步非阻塞。
- 服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求是才启动一个线程进行处理。
- 适合链接数目较多且比较短(轻操作)的架构,比如聊天服务器,jdk1.4开始支持。
AIO
- 异步非阻塞。
- 服务器实现模式为一个有效请求一个线程,客户端的IO请求都是有OS完成后再通知服务器应用去启动线程处理。
- 适用于链接数目多且连接较长(重操作)的架构,比如相册服务器,jdk1.7开始。
IO和NIO区别
- IO面向流,NIO面向缓冲区。
- IO阻塞,NIO非阻塞。
- NIO的选择器允许一个单独的线程来监视多个输入通道。
序列化和反序列化
定义
- 序列化:把对象转换为字节序列。
- 反序列化:字节序列恢复为对象。
用途
- 把对象的字节序列永久的保存在硬盘上,通常放在一个文件里。
- 在网络上传送对象的字节序列,因为无论何种类型的数据,都会以二进制序列的形式在网络上传输。
- 深复制。
深复制
@Override
public Graph clone() {
try {
// 序列化
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Graph) ois.readObject();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Java内存模型
javaGC机制
类的加载机制和双亲委派机制
copyOnRight
概念
- 当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。
好处
- 我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。