java核心技术 第一卷

早上收到腾讯的鄙视以后 觉得应该慢慢补基础

放下手头的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). 一个参数既可以是const还可以是volatile吗?解释为什么。
2). 一个指针可以是volatile 吗?解释为什么。
3). 下面的函数被用来计算某个整数的平方,它能实现预期设计目标吗?如果不能,试回答存在什么问题:
1
2
3
4
int  square( volatile  int  *ptr)
{
     return  ((*ptr) * (*ptr));
}
下面是答案:
1). 是的。一个例子是只读的 状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2). 是的。尽管这并不很常见。一个例子是当一个中断服务子程序修改一个指向一个buffer的 指针时。
3). 这段代码是个恶作剧。这段代码的目的是用来返 指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数, 编译器将产生类似下面的代码:
1
2
3
4
5
6
7
int  square( volatile  int  *ptr)
{
     int  a,b;
     a = *ptr;
     b = *ptr;
     return  a*b;
}
由于*ptr的值可能在两次取值语句之间发生改变,因此a和b可能是不同的。结果,这段代码可能返回的不是你所期望的平方值!正确的代码如下:
1
2
3
4
5
6
long  square( volatile  int *ptr)
{
     int  a;
     a = *ptr;
     return  a*a;
}
v 阻塞队列
LinkedBlockingQueue 无容量限制
LinkedBlockingDeque 双端
ArrayBlockingQueue 公平参数 指定容量
PoriorityBlockingQueue  带优先级
DelayQueue
vi Callable future :带参数返回的runnable和 返回的参数  可以抛出异常

下面来看一个简单的例子:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class CallableAndFuture {  
  2.     public static void main(String[] args) {  
  3.         Callable<Integer> callable = new Callable<Integer>() {  
  4.             public Integer call() throws Exception {  
  5.                 return new Random().nextInt(100);  
  6.             }  
  7.         };  
  8.         FutureTask<Integer> future = new FutureTask<Integer>(callable);  
  9.         new Thread(future).start();  
  10.         try {  
  11.             Thread.sleep(5000);// 可能做一些事情  
  12.             System.out.println(future.get());  
  13.         } catch (InterruptedException e) {  
  14.             e.printStackTrace();  
  15.         } catch (ExecutionException e) {  
  16.             e.printStackTrace();  
  17.         }  
  18.     }  
  19. }  
        FutureTask实现了两个接口,Runnable和Future,所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值,那么这个组合的使用有什么好处呢?假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,那么就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到,岂不美哉!这里有一个Future模式的介绍: http://openhome.cc/Gossip/DesignPattern/FuturePattern.htm

        下面来看另一种方式使用Callable和Future,通过ExecutorService的submit方法执行Callable,并返回Future,代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class CallableAndFuture {  
  2.     public static void main(String[] args) {  
  3.         ExecutorService threadPool = Executors.newSingleThreadExecutor();  
  4.         Future<Integer> future = threadPool.submit(new Callable<Integer>() {  
  5.             public Integer call() throws Exception {  
  6.                 return new Random().nextInt(100);  
  7.             }  
  8.         });  
  9.         try {  
  10.             Thread.sleep(5000);// 可能做一些事情  
  11.             System.out.println(future.get());  
  12.         } catch (InterruptedException e) {  
  13.             e.printStackTrace();  
  14.         } catch (ExecutionException e) {  
  15.             e.printStackTrace();  
  16.         }  
  17.     }  
  18. }  
        代码是不是简化了很多,ExecutorService继承自Executor,它的目的是为我们管理Thread对象,从而简化并发编程,Executor使我们无需显示的去管理线程的生命周期,是JDK 5之后启动任务的首选方式。

        执行多个带返回值的任务,并取得多个返回值,代码如下:

[java]  view plain copy print ? 在CODE上查看代码片 派生到我的代码片
  1. public class CallableAndFuture {  
  2.     public static void main(String[] args) {  
  3.         ExecutorService threadPool = Executors.newCachedThreadPool();  
  4.         CompletionService<Integer> cs = new ExecutorCompletionService<Integer>(threadPool);  
  5.         for(int i = 1; i < 5; i++) {  
  6.             final int taskID = i;  
  7.             cs.submit(new Callable<Integer>() {  
  8.                 public Integer call() throws Exception {  
  9.                     return taskID;  
  10.                 }  
  11.             });  
  12.         }  
  13.         // 可能做一些事情  
  14.         for(int i = 1; i < 5; i++) {  
  15.             try {  
  16.                 System.out.println(cs.take().get());  
  17.             } catch (InterruptedException e) {  
  18.                 e.printStackTrace();  
  19.             } catch (ExecutionException e) {  
  20.                 e.printStackTrace();  
  21.             }  
  22.         }  
  23.     }  
  24. }        

 vii 执行器 与线程池

在Java开发中,我们接触到了好多池的技术,String类的对象池、Integer的共享池、连接数据库的连接池、Struts1.3的对象池等等,池的最终目的都是节约资源,以更小的开销做更多的事情,从而提高性能。

        我们的web项目都是部署在服务器上,浏览器端的每一个request就是一个线程,那么服务器需要并发的处理多个请求,就需要线程池技术,下面来看一下Java并发包下如何创建线程池。

        1.  创建一个可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程。

[java]  view plain copy print ?
  1. ExecutorService threadPool = Executors.newFixedThreadPool(3);// 创建可以容纳3个线程的线程池  
        2. 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。

[java]  view plain copy print ?
  1. ExecutorService threadPool = Executors.newCachedThreadPool();// 线程池的大小会根据执行的任务数动态分配  
        3. 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。

[java]  view plain copy print ?
  1. ExecutorService threadPool = Executors.newSingleThreadExecutor();// 创建单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程替代它继续执行任务  
 
        4. 创建一个可安排在给定延迟后运行命令或者定期地执行的线程池。 

[java]  view plain copy print ?
  1. ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);// 效果类似于Timer定时器  
        每种线程池都有不同的使用场景,下面看一下这四种线程池使用起来有什么不同。

        1. FixedThreadPool

[java]  view plain copy print ?
  1. import java.util.concurrent.ExecutorService;  
  2. import java.util.concurrent.Executors;  
  3. public class ThreadPoolTest {  
  4.     public static void main(String[] args) {  
  5.         ExecutorService threadPool = Executors.newFixedThreadPool(3);  
  6.         for(int i = 1; i < 5; i++) {  
  7.             final int taskID = i;  
  8.             threadPool.execute(new Runnable() {  
  9.                 public void run() {  
  10.                     for(int i = 1; i < 5; i++) {  
  11.                         try {  
  12.                             Thread.sleep(20);// 为了测试出效果,让每次任务执行都需要一定时间  
  13.                         } catch (InterruptedException e) {  
  14.                             e.printStackTrace();  
  15.                         }  
  16.                         System.out.println("第" + taskID + "次任务的第" + i + "次执行");  
  17.                     }  
  18.                 }  
  19.             });  
  20.         }  
  21.         threadPool.shutdown();// 任务执行完毕,关闭线程池  
  22.     }  
  23. }  
        输出结果:

[java]  view plain copy print ?
  1. 1次任务的第1次执行  
  2. 2次任务的第1次执行  
  3. 3次任务的第1次执行  
  4. 2次任务的第2次执行  
  5. 3次任务的第2次执行  
  6. 1次任务的第2次执行  
  7. 3次任务的第3次执行  
  8. 1次任务的第3次执行  
  9. 2次任务的第3次执行  
  10. 3次任务的第4次执行  
  11. 2次任务的第4次执行  
  12. 1次任务的第4次执行  
  13. 4次任务的第1次执行  
  14. 4次任务的第2次执行  
  15. 4次任务的第3次执行  
  16. 4次任务的第4次执行  
        上段代码中,创建了一个固定大小的线程池,容量为3,然后循环执行了4个任务,由输出结果可以看到,前3个任务首先执行完,然后空闲下来的线程去执行第4个任务,在FixedThreadPool中,有一个固定大小的池,如果当前需要执行的任务超过了池大小,那么多于的任务等待状态,直到有空闲下来的线程执行任务,而当执行的任务小于池大小,空闲的线程也不会去销毁。
        2. CachedThreadPool

        上段代码其它地方不变,将newFixedThreadPool方法换成newCachedThreadPool方法。

        输出结果:

[java]  view plain copy print ?
  1. 3次任务的第1次执行  
  2. 4次任务的第1次执行  
  3. 1次任务的第1次执行  
  4. 2次任务的第1次执行  
  5. 4次任务的第2次执行  
  6. 3次任务的第2次执行  
  7. 2次任务的第2次执行  
  8. 1次任务的第2次执行  
  9. 2次任务的第3次执行  
  10. 3次任务的第3次执行  
  11. 1次任务的第3次执行  
  12. 4次任务的第3次执行  
  13. 2次任务的第4次执行  
  14. 4次任务的第4次执行  
  15. 3次任务的第4次执行  
  16. 1次任务的第4次执行  
        可见,4个任务是交替执行的,CachedThreadPool会创建一个缓存区,将初始化的线程缓存起来,如果线程有可用的,就使用之前创建好的线程,如果没有可用的,就新创建线程,终止并且从缓存中移除已有60秒未被使用的线程。

        3. SingleThreadExecutor        

       上段代码其它地方不变,将newFixedThreadPool方法换成newSingleThreadExecutor方法。       

       输出结果:

[java]  view plain copy print ?
  1. 1次任务的第1次执行  
  2. 1次任务的第2次执行  
  3. 1次任务的第3次执行  
  4. 1次任务的第4次执行  
  5. 2次任务的第1次执行  
  6. 2次任务的第2次执行  
  7. 2次任务的第3次执行  
  8. 2次任务的第4次执行  
  9. 3次任务的第1次执行  
  10. 3次任务的第2次执行  
  11. 3次任务的第3次执行  
  12. 3次任务的第4次执行  
  13. 4次任务的第1次执行  
  14. 4次任务的第2次执行  
  15. 4次任务的第3次执行  
  16. 4次任务的第4次执行  
        4个任务是顺序执行的,SingleThreadExecutor得到的是一个单个的线程,这个线程会保证你的任务执行完成,如果当前线程意外终止,会创建一个新线程继续执行任务,这和我们直接创建线程 不同,也和newFixedThreadPool(1)不同。

    4.ScheduledThreadPool    

[java]  view plain copy print ?
  1. import java.util.concurrent.ScheduledExecutorService;  
  2. import java.util.concurrent.TimeUnit;  
  3. public class ThreadPoolTest {  
  4.     public static void main(String[] args) {  
  5.         ScheduledExecutorService schedulePool = Executors.newScheduledThreadPool(1);  
  6.         // 5秒后执行任务  
  7.         schedulePool.schedule(new Runnable() {  
  8.             public void run() {  
  9.                 System.out.println("爆炸");  
  10.             }  
  11.         }, 5, TimeUnit.SECONDS);  
  12.         // 5秒后执行任务,以后每2秒执行一次  
  13.         schedulePool.scheduleAtFixedRate(new Runnable() {  
  14.             @Override  
  15.             public void run() {  
  16.                 System.out.println("爆炸");  
  17.             }  
  18.         }, 52, TimeUnit.SECONDS);  
  19.     }  
  20. }  
         ScheduledThreadPool可以定时的或延时的执行任务。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值