早上收到腾讯的鄙视以后 觉得应该慢慢补基础
放下手头的android 和thinking in java 开始看java 核心技术 这个帖子就当 做一点记录了
1 大数值
BigInteger 和BigDecimal
api:
BigInteger:add(BigInteger other)
BigInteger:substract(BigInteger other)
BigInteger:mutiply(BigInteger other)
BigInteger:divide(BigInteger other)
BigInteger:mod(BigInteger other) //余数
int compareTo(BigInteger other) //相等 返回0 不相等返回1
static BigInteger valueOf(long x)//返回一个X的大整数
BigDecimal同理 不过多了一个valueOf(long x,int scale)重载 表名X/10的scale次方
2 看到腾讯的面试的题的原题了(P121)
主要考的是按值调用 和按引用调用
例子
public static void tripleValue(double x)
{
x=x*3
}
double temp=10;
tripleValue(temp);
无论如何 temp还是10;
但是按照引用调用的时候会修改(即穿进去的位对象)
public static void tripleSalary(employee x)
{
x.raiseSalary(200);
}
harry = new employee (..);
tripleSalary(harry);
后就会改变对象里面的值;
具体内存过程 见图
大概就能明白了
觉得自己还有好多路要走啊 不过现在还不晚 找工作还有4个月 加油~
3类设计技巧
i 保证数据私有 :绝对不能破坏封装性
ii一定要对数据初始化:可以在构造器里设置默认值 (代码块也可以)
iii不要在类中使用过多的数据类型:即使用其他的类代替多个相关的基本类型的使用,感觉类似于数据库的三范式
例子
一个使用一个address的类来代替如下实例域:
private String street;
private String city;
private String state;
iv 不是所有的域都余姚独立的域访问器和域更改器:有些东西一旦设置了就不容许更改 有些东西 可以更改 但是不能获取
v 将职责过多的类进行分解:不同人理解不同(暂且搁置)
vi 类名和方法名要能够体现它们的职责;
4 继承
i 使用final 阻止子类继承 以及修改
ii 强类型转换 即 舍去一些信息 子类转为超类
iii抽象 和 受保护访问 不提供实现的超类和方法设置为 抽象 供子类继承 重写 ;一般来说 类中的域标记为private 方法标记为public 可是有特殊情况的话 为了只让子类访问某些方法 或者某些域 会使用protect
iv equasl tostring 的重写以及hashcode 你可以重写自己类 的这方法来覆盖(所有类的父类object的这俩方法)来方便自己调用
v 泛型和枚举 就是arraylist 和 enum 没什么好说的
vi反射 有Modifier、Constructor、Field、Method四个类,可以分别得到类的存取修饰符,构造函数,类成员,类方法的相关信息.
通过 getModifiers getConstructors getFields getMethods 获取所对应的信息集
实例代码如下 :
import java.util.*;
import java.lang.reflect.*;
/**
* This program uses reflection to print all features of a class.
* @version 1.1 2004-02-21
* @author Cay Horstmann
*/
public class ReflectionTest
{
public static void main(String[] args)
{
// read class name from command line args or user input
String name;
if (args.length > 0) name = args[0];
else
{
Scanner in = new Scanner(System.in);
System.out.println("Enter class name (e.g. java.util.Date): ");
name = in.next();
}
try
{
// print class name and superclass name (if != Object)
Class cl = Class.forName(name);
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print("class " + name);
if (supercl != null && supercl != Object.class) System.out.print(" extends "
+ supercl.getName());
System.out.print("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
System.exit(0);
}
/**
* Prints all constructors of a class
* @param cl a class
*/
public static void printConstructors(Class cl)
{
Constructor[] constructors = cl.getDeclaredConstructors();
for (Constructor c : constructors)
{
String name = c.getName();
System.out.print(" ");
String modifiers = Modifier.toString(c.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(name + "(");
// print parameter types
Class[] paramTypes = c.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
/**
* Prints all methods of a class
* @param cl a class
*/
public static void printMethods(Class cl)
{
Method[] methods = cl.getDeclaredMethods();
for (Method m : methods)
{
Class retType = m.getReturnType();
String name = m.getName();
System.out.print(" ");
// print modifiers, return type and method name
String modifiers = Modifier.toString(m.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(retType.getName() + " " + name + "(");
// print parameter types
Class[] paramTypes = m.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
/**
* Prints all fields of a class
* @param cl a class
*/
public static void printFields(Class cl)
{
Field[] fields = cl.getDeclaredFields();
for (Field f : fields)
{
Class type = f.getType();
String name = f.getName();
System.out.print(" ");
String modifiers = Modifier.toString(f.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.println(type.getName() + " " + name + ";");
}
}
}
vii setAccessible 和invoke
setAccessible :当反射的方法或者域为私有时,需要使用 f.setAccessible();
invoke:容许调用包装在当前method对象中的方法;//这块还是有点模糊的
viii 集成设计的技巧
(1)将公共操作和域放在超类
(2)不要使用受保护的域
(3)使用继承实现“is-a”关系 :即严格规范
(4)除非所有继承的方法都有意义,否则不要使用继承;
(5)在覆盖方法时,不要改变预期的行为;
(6)使用多态,而非类型信息
(7)不要过多的使用反射 //我能说我还没用过么 cry~
5接口 内部类
i 接口 不能使用new 实例化一个接口 但是却能声明接口的变量 然后再实例化(即 接口 x; x= new 集成接口的类()); 类中的方法和域都默认是public
ii 对象克隆 拷贝时 原变量与拷贝变量引用同一个对象 ,这时候需要克隆 关系如下
默认的clone方法是浅拷贝,所以必须要重新定义clone方法,一遍实现深拷贝;所以 类必须
(1)实现cloneable接口
(2)使用public访问修饰符重新定义clone方法
实现如下:
import java.util.*;
/**
* This program demonstrates cloning.
* @version 1.10 2002-07-01
* @author Cay Horstmann
*/
public class CloneTest
{
public static void main(String[] args)
{
try
{
Employee original = new Employee("John Q. Public", 50000);
original.setHireDay(2000, 1, 1);
Employee copy = original.clone();
copy.raiseSalary(10);
copy.setHireDay(2002, 12, 31);
System.out.println("original=" + original);
System.out.println("copy=" + copy);
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
}
}
}
class Employee implements Cloneable
{
public Employee(String n, double s)
{
name = n;
salary = s;
hireDay = new Date();
}
public Employee clone() throws CloneNotSupportedException
{
// call Object.clone()
Employee cloned = (Employee) super.clone();
// clone mutable fields
cloned.hireDay = (Date) hireDay.clone();
return cloned;
}
/**
* Set the hire day to a given date.
* @param year the year of the hire day
* @param month the month of the hire day
* @param day the day of the hire day
*/
public void setHireDay(int year, int month, int day)
{
Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();
// Example of instance field mutation
hireDay.setTime(newHireDay.getTime());
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public String toString()
{
return "Employee[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]";
}
private String name;
private double salary;
private Date hireDay;
}
注意 其中对clone的重写;
iii 接口和回调 感觉类似于 android的onclicklistener //暂且这么理解
iv 内部类 作用:内部类方法可以访问该定义所在的作用域中的数据,包括私有数据;内部了可以对同一个包中的其他类隐藏起来;当想要定义一个回调函数切不想编写大量代码时,使用匿名内部类比较便捷// android的setOnClickListener(new OnClickListener(){});
v静态内部类 使用内部类只是为了隐藏到另外一个类的内部,并不需要引用外围类对象,将内部类声明为satic
vi 代理 //这本书上讲的不太仔细 翻出了thinking in java 其中第七章讲了 组合继承和代理
我的理解为 当B要实现A里面的一些功能 但是A不想把自己的全部功能 提供给B 就给B提供代理 支持他所需要的功能
6 异常 断言 日志 和调试(ps本应该是swing和applet的 但是这属于技不属于术 我就跳过了)
i应该要考虑到的错误:
(1)用户输入错误
(2)设备错误
(3)物理限制
(4)代码错误
ii异常分类
派生于runtimeException的异常:
(1)错误的类型转换
(2)数组访问越界
(3)访问空指针
不是派生于untimeException的异常:
(1)试图在文件尾部后面读取出具
(2)试图打开一个不存在的文件
(3)试图根据给定的字符串查找class对象,但是这个字符串表示的类并不存在
III finally 无论是否发生异常 总会执行的方法 用于资源回收以及释放链接(finally 使用return会覆盖原有的return)
iv 分析堆栈跟踪元素 :即方法调用的列表
v 使用异常的技巧
(1)异常处理不能代替简单的测试 (栈的isempty以及emptystackexception)
(2)不要过分细化异常
(3)利用异常层次结构
(4)不要压制异常
(5)在检测错误时,“苛刻”要比放任更好
(6)不要羞于传递异常
vi 断言 :断言失败是致命的,不可恢复的错误;断言检查只用于开发和测试阶段;
vii 日志
(1)级别: severe 、warning、info、config、fine、finer、finest
(2)consolehandler
viii 调试技巧
(1)简单的打印
(2)每个类中放置main 单元测试
*(3)junit
(4)日志代理
(5)利用throwable提供的printstacktrace方法 可以从一个异常对象中获得堆栈的情况
(6)堆栈跟踪显示在System。err上,也可以利用printstacktrace方法将他发送到一个文件中
(7)将错误信息保存到一个文件中
(8)使用-verbose启动javaxuniji可以观察类的加载过程
(9)Xlint
(10)jconsole processID可以查看虚拟机性能
(11)jmap
(12)xprof 剖析代码中经常被调用的方法
7 泛型
i 类型变量的限定 public static <T extends Comparable>T min(T[] a)
ii 泛型代码和虚拟机 :虚拟机没有泛型类型对象 翻译:擦除类型变量,替换为限定类型(无限定的用object)
iii 约束和局限性
(1)不能使用基本类型实例化类型那个参数 即不能用int 要用interger
(2)运行是类型查询值适用于原始类型 即 Pair<String> stringpair .getClass()== Pair<Employee> employeePair.getClass()
(3)不能创建参数化类型的数组 Pair<String>[] table=new Pair <String>[10]//报错
(4)Varargs
(5)不能实例化类型变量 :
public Pair(){frist =new t();second=new t()};//error
frist =t.class.newInstance();//error
public static<T extends Comparable>T[] minmax(T...a)
{
T[] mm=(T[])Array.newInstance(a.getClass().getComponentType(),2);//通过
}
(6)泛型类的静态上下文中类型变量无效
(7)不能跑出或捕获泛型类异常
(8)擦除后的冲突
iv 通配符 即 pair<? extends employee>
(1)超类型限定: ? super Manager
(2)无限定通配符 :Pair<?>: ? getfrist(); void setFirst(?)
(3)通配符捕获:
v jvm中泛型类型信息:
8 集合 和算法:列表如下
linkedlist
Arraylist
arraydeque
hashset
treeset
priorityqueue
hashmap
treemap
i hashcode equals ==:
(1)绑定。当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
(2)绑定原因。Hashtable实现一个哈希表,为了成功地在哈希表中存储和检索对象,用作键的对象必须实现 hashCode
方法和 equals
方法。同(1),必须保证equals相等的对象,hashCode
也相等。因为哈希表通过hashCode检索对象。
(3)默认。
==默认比较对象在JVM中的地址。
hashCode 默认返回对象在JVM中的存储地址。
equal比较对象,默认也是比较对象在JVM中的地址,同==
ii 视图与包装器 视图类似于oracle中的视图 只做查询 修改无效(P598)
iii 算法 Collection.sort() Collection.shuffle() Collection.binarySearch()
iv位集 bitset 速度快 有些算法 可以用
9 多线程
i线程的状态
线程有6种状态,任何一个线程肯定处于这6种状态中的一种:
1) 产生(New):线程对象已经产生,但尚未被启动,所以无法执行。如通过new产生了一个线程对象后没对它调用start()函数之前。
2) 可执行(Runnable):每个支持多线程的系统都有一个排程器,排程器会从线程池中选择一个线程并启动它。当一个线程处于可执行状态时,表示它可能正处于线程池中等待排排程器启动它;也可能它已正在执行。如执行了一个线程对象的start()方法后,线程就处于可执行状态,但显而易见的是此时线程不一定正在执行中。
3)等待(waiting)(其他线程信号):当一个进程当代另一个线程通知调度器一个条件时,自己进入等待状态
4) 阻塞(Blocked)(资源 对象锁):当一个线程处于 阻塞状态时,系统排程器就会忽略它,不对它进行排程。当处于停滞状态的线程重新回到可执行状态时,它有可能重新执行。如通过对一个线程调用wait()函数后,线程就进入停滞状态,只有当两次对该线程调用notify或notifyAll后它才能两次回到可执行状态。
5)记时等待(timed waitting)同3 不过是计时器控制
6)终止(Terminated)当一个线程正常结束,它便处于死亡状态。如一个线程的run()函数执行完毕后线程就进入死亡状态;或者一个没有捕获的异常终止了run方法而意外死亡。
用法
sleep()
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
由于sleep()方法是Thread类的方法,因此它不能改变对象的机锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的机锁没有被释放,其他线程仍然无法访问这个对象。sleep()方法不需要在同步的代码块中执行。但是sleep()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
wait()和notify()和notifyAll()
wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对象。wait()必须在同步的代码块中执行。当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去了对象的机锁,可以允许其它的线程执行一些同步操作。但是wait()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
wait可以让同步方法或者同步块暂时放弃对象锁,而将它暂时让给其它需要对象锁的人(这里应该是程序块,或线程)用,这意味着可在执行wait()期间调用线程对象中的其他同步方法!在其它情况下(sleep啊,suspend啊),这是不可能的.但是注意我前面说的,只是暂时放弃对象锁,暂时给其它线程使用,我wait所在的线程还是要把这个对象锁收回来的呀.wait什么?就是wait别人用完了还给我啊!
好,那怎么把对象锁收回来呢?
第一种方法,限定借出去的时间.在wait()中设置参数,比如wait(1000),以毫秒为单位,就表明我只借出去1秒中,一秒钟之后,我自动收回.
第二种方法,让借出去的人通知我,他用完了,要还给我了.这时,我马上就收回来.哎,假如我设了1小时之后收回,别人只用了半小时就完了,那怎么办呢?靠!当然用完了就收回了,还管我设的是多长时间啊.
那么别人怎么通知我呢?相信大家都可以想到了,notify(),这就是最后一句话"而且只有在一个notify()或notifyAll()发生变化的时候,线程才会被唤醒"的意思了.
notify()唤醒在此对象监视器上等待的单个线程。当它被一个notify()方法唤醒时,等待池中的线程就被放到了锁池中。该线程将等待从锁池中获得机锁,然后回到wait()前的中断现场。
notifyAll()唤醒在此对象监视器上等待的所有线程。
suspend和resume()
join()
join()方法使当前线程停下来等待,直至另一个调用join方法的线程终止。值得注意的是,线程的在被激活后不一定马上就运行,而是进入到可运行线程的队列中。但是join()可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException。
yield()
Yield()方法是停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么Yield()方法将不会起作用。
interrupt()
interrupt()中断线程。需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。
ii 线程优先级 最小为1 最大为10 默认为5
iii 守护线程
iv 同步
(1) lock.lock()和lock.n
public class LockTest {
public static void main(String[] args) {
final Outputter1 output = new Outputter1();
new Thread() {
public void run() {
output.output("zhangsan");
};
}.start();
new Thread() {
public void run() {
output.output("lisi");
};
}.start();
}
}
class Outputter1 {
private Lock lock = new ReentrantLock();// 锁对象
public void output(String name) {
// TODO 线程输出方法
lock.lock();// 得到锁
try {
for(int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
} finally {
lock.unlock();// 释放锁
}
}
}
(2)synchronized
public synchronized void output(String name) {
// TODO 线程输出方法
for(int i = 0; i < name.length(); i++) {
System.out.print(name.charAt(i));
}
代替上面的功能
(3)监视器 :只包含私有域的类 所有对象 方法都有锁
(4)volatile 为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值. 即使完全不加锁 不同步 随时取值的
:
1
2
3
4
|
int
square(
volatile
int
*ptr)
{
return
((*ptr) * (*ptr));
}
|
1
2
3
4
5
6
7
|
int
square(
volatile
int
*ptr)
{
int
a,b;
a = *ptr;
b = *ptr;
return
a*b;
}
|
1
2
3
4
5
6
|
long
square(
volatile
int
*ptr)
{
int
a;
a = *ptr;
return
a*a;
}
|
下面来看一个简单的例子:
FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,那么这个组合的使用有什么好处呢?假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到,岂不美哉!这里有一个Future模式的介绍: http://openhome.cc/Gossip/DesignPattern/FuturePattern.htm 。
下面来看另一种方式使用Callable和Future,通过ExecutorService的submit方法执行Callable,并返回Future,代码如下:
代码是不是简化了很多,ExecutorService继承自Executor,它的目的是为我们管理Thread对象,从而简化并发编程,Executor使我们无需显示的去管理线程的生命周期,是JDK 5之后启动任务的首选方式。
执行多个带返回值的任务,并取得多个返回值,代码如下:
vii 执行器 与线程池
在Java开发中,我们接触到了好多池的技术,String类的对象池、Integer的共享池、连接数据库的连接池、Struts1.3的对象池等等,池的最终目的都是节约资源,以更小的开销做更多的事情,从而提高性能。
我们的web项目都是部署在服务器上,浏览器端的每一个request就是一个线程,那么服务器需要并发的处理多个请求,就需要线程池技术,下面来看一下Java并发包下如何创建线程池。
1. 创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。
- ExecutorService threadPool = Executors.newFixedThreadPool(3);// 创建可以容纳3个线程的线程池
- ExecutorService threadPool = Executors.newCachedThreadPool();// 线程池的大小会根据执行的任务数动态分配
- ExecutorService threadPool = Executors.newSingleThreadExecutor();// 创建单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程替代它继续执行任务
- ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);// 效果类似于Timer定时器
1. FixedThreadPool
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- public class ThreadPoolTest {
- public static void main(String[] args) {
- ExecutorService threadPool = Executors.newFixedThreadPool(3);
- for(int i = 1; i < 5; i++) {
- final int taskID = i;
- threadPool.execute(new Runnable() {
- public void run() {
- for(int i = 1; i < 5; i++) {
- try {
- Thread.sleep(20);// 为了测试出效果,让每次任务执行都需要一定时间
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("第" + taskID + "次任务的第" + i + "次执行");
- }
- }
- });
- }
- threadPool.shutdown();// 任务执行完毕,关闭线程池
- }
- }
- 第1次任务的第1次执行
- 第2次任务的第1次执行
- 第3次任务的第1次执行
- 第2次任务的第2次执行
- 第3次任务的第2次执行
- 第1次任务的第2次执行
- 第3次任务的第3次执行
- 第1次任务的第3次执行
- 第2次任务的第3次执行
- 第3次任务的第4次执行
- 第2次任务的第4次执行
- 第1次任务的第4次执行
- 第4次任务的第1次执行
- 第4次任务的第2次执行
- 第4次任务的第3次执行
- 第4次任务的第4次执行
2. CachedThreadPool
上段代码其它地方不变,将newFixedThreadPool方法换成newCachedThreadPool方法。
输出结果:
- 第3次任务的第1次执行
- 第4次任务的第1次执行
- 第1次任务的第1次执行
- 第2次任务的第1次执行
- 第4次任务的第2次执行
- 第3次任务的第2次执行
- 第2次任务的第2次执行
- 第1次任务的第2次执行
- 第2次任务的第3次执行
- 第3次任务的第3次执行
- 第1次任务的第3次执行
- 第4次任务的第3次执行
- 第2次任务的第4次执行
- 第4次任务的第4次执行
- 第3次任务的第4次执行
- 第1次任务的第4次执行
3. SingleThreadExecutor
上段代码其它地方不变,将newFixedThreadPool方法换成newSingleThreadExecutor方法。
输出结果:
- 第1次任务的第1次执行
- 第1次任务的第2次执行
- 第1次任务的第3次执行
- 第1次任务的第4次执行
- 第2次任务的第1次执行
- 第2次任务的第2次执行
- 第2次任务的第3次执行
- 第2次任务的第4次执行
- 第3次任务的第1次执行
- 第3次任务的第2次执行
- 第3次任务的第3次执行
- 第3次任务的第4次执行
- 第4次任务的第1次执行
- 第4次任务的第2次执行
- 第4次任务的第3次执行
- 第4次任务的第4次执行
4.ScheduledThreadPool
- import java.util.concurrent.ScheduledExecutorService;
- import java.util.concurrent.TimeUnit;
- public class ThreadPoolTest {
- public static void main(String[] args) {
- ScheduledExecutorService schedulePool = Executors.newScheduledThreadPool(1);
- // 5秒后执行任务
- schedulePool.schedule(new Runnable() {
- public void run() {
- System.out.println("爆炸");
- }
- }, 5, TimeUnit.SECONDS);
- // 5秒后执行任务,以后每2秒执行一次
- schedulePool.scheduleAtFixedRate(new Runnable() {
- @Override
- public void run() {
- System.out.println("爆炸");
- }
- }, 5, 2, TimeUnit.SECONDS);
- }
- }
viii fork-join :类似于map reduce
让我们通过一个简单的需求来使用下Fork/Join框架,需求是:计算1+2+3+4的结果。
使用Fork/Join框架首先要考虑到的是如何分割任务,如果我们希望每个子任务最多执行两个数的相加,那么我们设置分割的阈值是2,由于是4个数字相加,所以Fork/Join框架会把这个任务fork成两个子任务,子任务一负责计算1+2,子任务二负责计算3+4,然后再join两个子任务的结果。
因为是有结果的任务,所以必须继承RecursiveTask,实现代码如下:
通过这个例子让我们再来进一步了解ForkJoinTask,ForkJoinTask与一般的任务的主要区别在于它需要实现compute方法,在这个方法里,首先需要判断任务是否足够小,如果足够小就直接执行任务。如果不足够小,就必须分割成两个子任务,每个子任务在调用fork方法时,又会进入compute方法,看看当前子任务是否需要继续分割成孙任务,如果不需要继续分割,则执行当前子任务并返回结果。使用join方法会等待子任务执行完并得到其结果。
ix 同步器 //P688(1)信号量 mutex
(2)倒计时门栓
(3)障栅
(4)交换器
(5)同步队列
至此 上卷 阅读 结束 找工作前 应该过第二遍 到第三遍
2015 4.27