最近对Core Java基础作了一些学习。有本身的看法,也有别人的总结,供你们参考。java
其实这个问题并不难,只是在这里作一个总结。一共有三种。程序员
Runnable
接口,并实现该接口的run()
方法Thread
类,重写run()
方法Callable
接口,实现call()
方法。你们可能对前两种已经很清楚了,重点说下第三种。
Callable
接口是属于Executor
框架中的类,Callable
接口与Runnable
接口相似,但比后者功能更增强大,主要有三点:算法
Callable
能够在任务结束后提供一个返回值,Runnable
没法提供这个功能;Callable
的call()
方法能够抛出异常,Runnable
接口的run()
方法不能抛出异常;Callable
能够获得一个Future
对象,Future
对象表示异步计算的结果。它提供了检查计算是否完成的方法。因为线程属于异步计算模型,因此没法从其余线程中获得方法的返回值。在这种状况下,就可使用Future
来监视目标线程调用call()
方法的状况。当调用Futrue
的get()
方法以获取结果时,当前线程就会阻塞,直到call()
方法结束返回结果。举个例子,此代码在JDK 8 下运行,由于使用了lambda表达式:编程
package exam; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class CallableAndFuture { public static void main(String[] args) { ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 启动线程 Future<String> future = threadPool.submit(() -> "Hello, world"); try { System.out.println("waiting thread to finish."); System.out.println(future.get()); // 等待线程结束,并获取返回结果 threadPool.shutdown(); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }
volatile
关键字的做用在Java语言中,有时候为了提升程序的运行效率,编译器会作一些优化操做,把常常被访问的变量缓存起来,程序在读取这个变量的时候又有可能直接从寄存器中读取这个值,而不会去内存中读取。这样的好处提升了程序的运行效率,但当遇到多线程编程时,变量的值可能被其余线程改变了,而该缓存的值不会作相应的改变,从而致使应用程序读取的值可能与实际的变量值不一致。关键字volatile
正好解决这个问题,被volatile
修饰的变量编译器不会作优化,每次都会从内存读取。缓存
常常会遇到一个这样的代码,new一个子类,其子类以及父类每一个属性和方法的执行顺序,具体能够看如下例子:安全
** * Java程序初始化工做能够在许多不一样的代码中来完成,它们执行的顺序以下: * 父类静态变量 * 父类静态代码块 * 子类静态变量 * 子类静态代码块 * 父类非静态变量 * 父类非静态代码块 * 父类构造函数 * 子类非静态变量 * 子类非静态代码块 * 子类构造函数 * * * 注意,只有方法具备多态性,属性则没有。 * @author TurtusLi * */ class BaseI { int num = 1; public BaseI() { this.print(); num = 2; } public void print() { System.out.println("Base.num = " + num); } } public class Example1423 extends BaseI { int num = 3; public Example1423() { this.print(); num = 4; } // 去掉这个复写方法,运行看效果 @Override public void print() { System.out.println("Sub.num = " + num); } public static void main(String[] args) { BaseI b = new Example1423(); System.out.println(b.num); } }
switch
语句支持String
类型的实现原理在Java 7 之后,switch
语句能够用做String
类型上。多线程
从本质来说,switch
对字符串的支持,其实也是int
类型值的匹配。它的实现原理以下:并发
经过对case
后面的String
对象调用hashCode(
)方法,获得一个int类型的Hash值,而后用这个Hash值来惟一标识着这个case
。框架
那么当匹配的时候,首先调用这个字符串的hashCode()
方法,获取一个Hash值(int
类型),用这个Hash值来匹配全部的case
,若是没有匹配成功,说明不存在;若是匹配成功了,接着会调用字符串的equals()
方法进行匹配。异步
由此看出,String
变量不能是null;同时,switch
的case
子句中使用的字符串也不能为null。
Java主要提供了三种实现同步机制的方法。
synchronized
关键字。有两种用法,能够是synchronized
方法和synchronized
代码块。wait
和notify
方法。Lock
。Lock
接口有一个实现类ReentrantLock
,也能够实现多线程的同步。volatile
代替synchronized
,近可能使用volatile
。由于被synchronized
修饰的方法或代码块在同一时间只能容许一个线程访问,而volatile
没有这个限制,所以使用synchronized
会下降并发量。因为volatile
没法保证原子性操做,所以在多线程的状况下,只有对变量的操做为原子操做的状况下才可使用volatile
。synchronized
块内的代码。ConcurrentHashMap
)来代替synchronized
容器(Hashtable
)。他们的主要区别是fail-safe容许在遍历的过程当中对容器的数据进行修改,而fail-fast则不容许。下面分别介绍这两种迭代器的工做原理。
fail-fast:直接在容器上进行遍历,在遍历的过程当中,一旦发现容器中的数据被修改了(添加元素、删除或修改元素),就会抛出ConcurrentModificationException
异常致使遍历失败。常见的使用fail-fast的容器有HashMap
和ArrayList
等。
fail-safe:这种遍历是基于容器的克隆。所以,对容器中内容的修改不影响遍历。常见使用fail-safe方式的容器有ConcurrentHashMap
和CopyOnWriteArrayList
。
关于JVM的知识,有一本很是好的书籍——周志明《深刻理解Java虚拟机:JVM高级特性与最佳实践(第2版)》,里面有很是好的介绍。几乎能够说是Java程序员必读书籍。
虚拟机栈是线程私有的,当建立一个线程时,同时会新建一个虚拟机栈,它描述的是Java方法执行的内存模型。 栈中有一个很是重要的概念——栈帧。栈帧用于保存局部变量表,操做数栈,方法出口等。
其实栈溢出最简单的方式是无限递归。
堆内存是线程共享的,是JVM中内存管理的最大一块内存,它保存全部实例化的对象。
堆内存溢出最简单的方式是不停的new对象,GC来不及回收,直到内存所有耗尽。
方法区也是内存共享的。它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
方法区溢出简单的方式是,调用String
类的intern()
方法,此方法若是在堆区找不到已经存在的String
对象的话,就会往方法区中的常量池放一份,而后返回其引用放在堆区。还有一种办法是不停地加载类。
int i =0; i=i++;
语句中,i=i++
是线程安全的吗?若是不安全,请说明上面操做在JVM中的执行过程,为何不安全?说出JDK中哪一个类能达到以上程序的效果,而且是线程安全且高效的,简述其原理。语句i=i++
的执行过程:先把i
的值取出来放到栈顶,能够理解为引入了第三方变量k
,此时,k
的值为i
,而后执行自增操做,因而i
的值变为1
,最后执行赋值操做i=k
(自增前的值),所以,执行结束后,i
的值仍是0
。从上面的分析得知,i=i++
语句的执行过程是由多个操做组成,它不是原子操做,所以,不是线程安全的。
在Java中,++i
和i++
操做并非线程安全的,在使用的时候,不可避免地会用到synchronized
关键字。而AtomicInteger
是一个提供原子操做的Integer
类,它提供了线程安全且高效的原子操做,是线程安全的,其底层的原理是利用处理器的CAS(Compare And Swap,比较与交换,一种有名的无锁算法)操做来检测栈中的值是否被其余线程改变,若是被改变,则CAS操做失败。这种实现方法在CPU指令级别实现了原子操做,所以,它比使用synchronized来实现同步效率更高。