Java编程思想

   初读Java编程思想,因为我以前都是经过网络视频的方式来学习Java知识,因此此书初读起来有些晦涩难懂,可是我仍是尽力去看,而后记下我初看时以为对我有用的笔记和一些本身的理解。java

 

第一章  对象导论

1.多态
  在处理类型的层次结构时,把一个对象不当作它所属的特定类型来对待,而是将其当作其基类(父类)的对象来对待。这使得人们能够编写出不依赖于特定类型的代码。c++


2.单继承
  单继承结构保证全部对象都具有某些功能,能够在每一个对象上执行某些基本操做。程序员


3.参数化类型
  一个编译器能够自动定制做用于特定类型上的类,用于向下转型,如Circle是一种Shape类型,可是不知道某个Object是Circle仍是Shape,此时就须要参数化类型,其实就是泛型。编程


4.对象的建立和生命周期
  当处理完某个对象以后,系统某个其余部分可能还在处理它。java提供了被称为“垃圾回收器”的机制,它能够自动发现对象什么时候再也不被使用,并继而销毁它。能够避免暗藏的内存泄露问题。数组


5.并发编程
  并发:指应用可以交替执行不一样的任务,例:吃完饭->喝水 喝完水->睡觉 一觉醒来->吃饭......
  并行:指应用可以同时执行不一样的任务,例:吃饭的时候能够边吃饭边打电话,这两件事情能够同时执行。
  并行存在锁的概念,如两个相同线程并行访问到同一资源,该资源只能被一个线程使用,该资源会被锁定,被一个线程占用时,该任务锁定该项资源,完成任务,而后释放资源锁,使其它任务可使用这项资源。缓存

 

第二章  一切都是对象

1.对象存储到什么地方
  (1)寄存器
  (2)堆栈(RAM区):堆栈指针向下移动,分配新的内存,向上移动,则释放内存。
  (3)堆(RAM区):一种通用内存池。
   堆不一样于堆栈的好处是:编译器不须要知道存储的数据在堆里存活多长时间。
   坏处:用堆进行存储分配和清理可能比用堆栈进行存储分配须要更多的时间。
  (4)常量存储
   直接存放在代码内部。
  (5)非RAM存储
   数据存活在程序以外,如磁盘。
  2.高精度数字
  BigInteger:支持任意精度的整数。
  BigDecimal:支持任何精度的定点数。
  3.static
  修饰代码块:先于构造器加载,而且只会加载一次,方法内没有this,全局方法,仅经过类自己来调用static方法。安全

 

第三章  操做符

第四章  控制执行流程

  第3、四两章我以为很基础,因此没有笔记。网络

 

第五章  初始化与清理

1.构造器:这是一个在建立对象时被自动调用的特殊方法,确保初始化,java会在用户有能力操做对象以前自动调用相应的构造器,由编译器调用,与类名相同,无返回值。
(1)不接受任何参数的构造器叫作默认构造器。
(2)若是你没有定义构造器,编译器自动帮你建立默认构造器,可是若是你定义了构造器,编译器就不会再去建立默认构造器。闭包

  例子:并发

class Bird2{
   Bird2(int f){}
   Bird(double d){}
}
public class NoSynthesis{
    public static void main(String[] args){
       Bird2 b=new Bird2();//报错,没有默认构造器
       Bird2 b2=new Bird2(1);
       Bird2 b3=new Bird2(1.8);
      }
}

(3)除构造器以外,编译器禁止在其余任何方法中调用构造器。


2.this:指出当前对象的引用  如 A a=new A();指出的就是引用a。


3.垃圾回收器:垃圾回收器只会释放那些经由new分配的内存。


4.finalize()方法:并不是使用new得到的内存区域时,使用该方法释放内存,该区域主要指在java代码中调用了非java代码。也许会调用c的malloc()函数来分配空间,要释放空间得调用free(),但该方法是c、c++中才有的,因此在java中要使用finalize()。当垃圾回收器准备好释放对象占用的存储空间,将首先调用finalize()方法,而且在下一次垃圾回收动做发生时,才会真正回收对象占用的内存。在作清理工做时,咱们能够在finalize()里加入某种擦除功能,当垃圾回收发生时,finalize()获得调用,数据就会被擦除,要是垃圾回收没有发生,数据就会保留。
(1)垃圾回收只与内存有关,也就是说使用垃圾回收器的惟一缘由是为了回收程序不在使用的内存。不管是垃圾回收,仍是终结,都不保证必定会发生。若是jvm并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以恢复内存的。


5.垃圾回收器如何工做:
(1)中止-复制
先暂停程序的运行,而后将全部存活的对象从当前堆复制到另外一堆,没有被复制的全是垃圾,当对象被复制到新堆时,它们是一个挨着一个的,因此新堆保持紧凑排列,而后就能够按像传送带同样的方法,每分配一个新对象,就往前移动一格,这样简单、直接地分配新空间了。当把对象从一处搬到另外一处时,全部指向它的引用都必须修正。
缺点:效率会下降。缘由是,首先,得有两个堆,而后在这两个分离的堆之间来回捣腾,从而得维护比实际须要多一倍的空间。第二个问题,是复制,若是程序只有少许垃圾,或者没垃圾,垃圾回收器仍然会将全部内存自一处复制到另外一处,这很浪费。
(2)标记-清扫
虚拟机进行检查,要是没有新垃圾产生,就会转向这一方式,该方式速度至关慢,可是当你知道只会产生少许垃圾甚至不会产生垃圾时,它的速度就很快了。
该方式所依据的思路一样是从堆栈和静态存储区出发,遍历全部的引用,进而找出全部存活的对象,每当它找到一个存活对象,就会给对象设一个标记,这个过程当中不会回收任何对象。只有所有标记工做完成的时候,清理才会开始。在清理过程当中,没有标记的对象将会被释放,不会发生任何复制动做,因此剩下的堆空间是不连续的,垃圾回收器要是但愿获得连续空间的话,就得从新整理剩下的对象。

内存会分配较大的“块”,中止-复制要把全部存活对象复制到新堆,会形成大量内存复制行为,有了块以后垃圾回收器,就能够往废弃的块里拷贝对象了,这种好处就是先将空间分配好,执行时,就不须要现去分配空间,减小时间,典型的空间换时间。

java实际垃圾清理是“自适应技术”:java虚拟机会进行监视,若是对象都很稳定,垃圾回收器的效率下降的话,就切换到“标记-清扫”方式,一样,java虚拟机会跟踪“标记-清扫”的效果,要是堆空间出现不少碎片,就会切换回“中止-复制”方式。

 

6.自动初始化:若是没给基本类型的字段赋值,编译器会给其赋一个默认值,自动初始化会在构造器被调用以前执行。

 

第六章  访问权限控制

1.重构:即重写代码,以使得它更可读、更易理解、并所以而更具可维护性。

 

第七章  复用类

1.带参数的构造器:

class Game{
  Game(int i){
     print("Game constructor");
 }
}

class BoarGame extends Game{
   BoarGame(int i){
        super(i)
        print("BoarGame  constructor");
   }
}


  在继承中构造器的执行顺序:先父类构造器,而后子类构造器,上面代码要说明父类写的是带参数的构造器,因此没有默认构造器,子类必须在自身构造器中调用父类的带参构造器,由于子类会调用父类的默认构造器,可是此时父类没有默认构造器,因此不调用父类的带参构造器的话,会报错。

2.名称屏蔽:
  若是java的基类拥有某个已被屡次重载的方法名称,那么在子类中从新定义该方法名称不会屏蔽其在基类中的任何版本。就是说父类中有一个方法的多个重载,子类也重载了该方法,它不会屏蔽掉父类的多个重载。@Override指重写,若是你不想重载,加这个就不容许重载,只能重写。

3.组合与继承:
  组合和继承都容许在新的类中放置子对象,组合是显示的这样作,而继承是隐式的作。

4.protected:
  就类用户而言,这是private的,但对于任何继承与此类的子类或其余任何位于同一个包内的类来讲,它是能够访问的。

5.向上转型:
  举个例子:乐器A 、钢琴B  、B继承A ,B拥有A的全部方法,那么就能够说B是A类型的,再通俗就是父类引用指向子类对象。即  A a=new B() , a引用指向了一个B对象,表示B对象是A类型的。
  延伸:向下转型
  A a=new A();
  B b=(B)a ;      表示A对象是B类型

6.final:
(1)一个永不改变的编译时常量。
(2)一个在运行时被初始化的值,而你不须要它被改变。
final修饰方法
  把方法锁定,防止任何继承类修改它的含义,即确保在继承中父类的方法不会被子类覆盖。
final修饰类
  你不容许被修饰的类有子类,即不能被继承。

 

第八章  多态

1.后期绑定:即多态实现机制,编译器一直不知道对象的类型,可是方法调用机制能找到正确的方法体,并加以调用。多态只能发生在方法调用时。当调用对象某个方法时,JVM查找该对象类的方法表以肯定该方法的直接引用地址,有了地址后才真正调用该方法。   

2.子类继承父类时,若是父类的一个方法私有,子类重写这个方法是不成功的,即便方法名同样也不能覆盖父类的这个方法,只能被当作一个新的方法去看待。

3.只有普通的方法调用是多态的,若是直接访问某个域,这个域就将在编译期进行解析。如子类和父类中有两个相同名字的基本类型 如int a,子类不会覆盖父类的int a,而是分配了不一样的存储空间,向上转型时,调用的就是父类的方法。
如 :

class Super{
          public int field=0;
          public int getField(){return field;}
}

class Sub extends Supoer{
          public int field=1;
          public int getField(){return field;}
          public int getSuperField(){return super.field;}
}
    //Super sup=new Sub();
    //sup.field=0,sup.getField()=1
    //Sub sub=new Sub();
    //sub.field=1,sub.getField()=1,sub.getSuperField=0;

  为何会这种结果:第一个是向上转型,调用的是Super这个类型的方法,因此field为0,sup.getField()=1是由于调用的是子类的方法,由于父类方法被子类重写了,当子类重写父类方法时,父类方法地址指向子类方法。

4.构造器与多态:构造器至关于static方法,只不过该static声明是隐式的,子类调用构造器前,要先调用父类的构造器。

5.继承与清理:通常清理由垃圾回收器本身去执行,但在某些必要条件下,要本身写清理方法,此时子类继承父类,父类中写了清理方法,可是子类存在特殊处理,因此重写父类清理方法,覆盖了父类清理,此时要注意,父类就不会被清理,因为是在子类中进行,父类的清理方法被覆盖掉了,因此在子类中没有执行父类的清理方法,因此必定要加上super的父类清理方法,不然父类的清理不会发生。

6.构造器内部的多态方法的行为:在继承中,最好将父类直接放到子类中看,这样直观。

class Glyph{
  void draw() { 
    print("G.draw()");
   }
  Glyph(){
   print("Glyph() before draw()");
   draw();
   print("Glyph() after draw()");
  }
}

class RoundGlyph extends Glyph{
   private int radius=1;
   RoundGlyph(int r){
    radius=r;
    print("RoundGlyph.RoundGlyph(),radius="+radius);
 }
    void draw(){
      print("RoundGlyph.draw,radius="+radius);
 }
}

public class PolyConstructors{
   public static void main(String[] args){  
     new RoundGlyph(5); 
 }
}

     //输出 Glyph() before draw()
     //RoundGlyph.RoundGlyph(),radius=0
     //Glyph() after draw()
     //RoundGlyph.RoundGlyph(),radius=5

 

  这种输出结果的理解,先是调用父类构造方法,走到draw()时,要把父类放到在子类中结合看,能够看到draw()被重写了,因此调用的是子类的draw(),radius为0是因为在一切事物发生以前,将分配给对象的存储空间初始化二进制的0,即在调用父类构造前,先分配存储空间,初始化的值为0,执行完父类构造后,再给radius赋值为5。

 

第九章  接口

1.抽象类和抽象方法:包含抽象方法的类叫作抽象类,若是一个类包含一个或多个抽象方法,该类必须被限定为抽象的,不然编译器会报错。抽象类主要用来被继承,不能被实例化的类。

2.使用接口的核心缘由:
(1)为了可以向上转型为多个基类型。
(2)防止客户端程序员建立该类的对象,并确保这仅仅是创建一个接口。
接口与抽象类之间的选择: 若是要建立不带任何方法定义和成员变量的基类,那么就应该选择接口,而非抽象类。

3.经过继承来扩展接口:一个类只能继承一个基类,但能实现多个接口,接口能够继承接口,而且能够多继承。

4.接口中的域:放入接口中的任何域都是自动static和final的。

 

第十章  内部类

1.连接到外部类:内部类自动拥有对其外围类全部成员的访问权,privte修饰的也可访问。
理解:就是内部类能直接访问外部类的信息,但外围类不能直接访问内部类里的信息。

2.使用.this与.new:建立一个内部类对象

DotNew dn=new DotNew();
DotNew.Inner dn1=dn.new Inner();


3.内部类与向上转型:一个类里面,含有一个被private修饰的内部类,外部类不能访问这个内部类,除了内部类本身。

4.在方法和做用域内的内部类:在一个做用域内的内部类,在做用域外,他是不可用的,除此以外,他与普通的类没有区别,做用域就是指{ }。

5.匿名内部类:
写法:

return new A(){};

在匿名内部类,他不可能有命名构造器,由于它根本没有名字。若是定义一个匿名内部类,而且但愿它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。

6.嵌套类:若是不须要内部类对象与其外围类对象之间有联系,那么能够将内部类声明为static,这一般称为嵌套类。普通的内部类对象隐式的保存了一个引用,指向建立它的外围对象。
嵌套类,意味着:
(1)要建立嵌套类的对象,并不须要其外围类的对象
(2)不能从嵌套类的对象中访问非静态的外围类对象
如2中建立一个内部类对象,只须要

Inner dn1=new Inner();

 

7.接口内部的类:接口里能够放一个接口的实现类,该类实际就是嵌套类,由于接口自动public和static。

8.为何须要内部类:完成间接的多继承实现,即一个类只能继承一个父类,这时这个类只有两个可用类型,即自己和父类类型,可是若是还想有另外几个类型的话,就能够在子类中写一个方法,方法里写一个类。
如 

class Z extends D{
      E makeE() { 
     return new E(){};
 }
}

这时若是

Z z=new Z();
z.makeE();

它z返回的类型就是E了。

9.闭包与回调:闭包是一个可调用的对象,它记录了一些信息,这些信息来自于建立它的做用域。内部类是面向对象的闭包,它不只包含外围类对象(建立内部类的做用域)的信息,还自动拥有一个指向外围类对象的引用,在此做用域内,内部类有权限操做全部的成员,包括private成员。

10.内部类的继承:

class WithInner{
   class Inner{
     }
}

public class InheritInner extends WithInner.Inner{
      InheritInner(WithInner wi){
        wi.super;
 }
}

 

  这种继承内部类的类,它的构造器,必须传入一个内部类的外围类的对象的引用,而后必须在构造器中使用*.super这个语法。估计是规定,没有为啥。

 

第十一章 持有对象

1.泛型和类型安全的容器
在<>能够放多个类型,不止一个。

编译期错误:指将java文件转为二进制的class失败。
运行时错误:转为二进制是成功的,在运行时出错。

泛型里面也能够添加它的子类

2.List

List:线性顺序存储元素、元素可重复、能够存放null、可查找
LinkedList:基于链表,中间插入删除快,按位置查找慢
ArrayList:基于数组,中间插入删除慢、按位置查找快,缘由是数组固定大小,插入时,要从新建一个数组

3.迭代器
Iterator: Iterator是一个迭代器接口,专门用来迭代各类Collection集合,包括Set集合和List集合。能够用来遍历不知道具体数目的list和set。
(1)next()得到序列中的下一个元素
(2)hashNext()检查序列中是否还有元素
(3)remove()将迭代器新近返回的元素删除

public static void main(String[] args)
    {
        List<String> list=new ArrayList<>();
        list.add("abc");
        list.add("edf");
        list.add("ghi");
        for(Iterator<String> it=list.iterator();it.hasNext();)
        {
            System.out.println(it.next());
        }
    }

 

第十二章 经过异常处理错误

1.异常说明
Java提供相应的语法(强制使用这个语法),使你能以礼貌的方式告知客户端程序员某个方法可能会抛出的异常类型。这种作法的好处是,为异常先占个位子,之后就能够抛出这种异常而不用修改已有的代码。

2.使用finally进行清理
不管异常是否被抛出,finally子句总能被执行。

3.finally用来作什么?
析构函数是“当对象再也不使用”会被调用的函数,java没有析构函数可供调用。java有垃圾回收机制,因此内存释放不是问题。当要把内存以外的资源恢复到它们的初始状态时,就要用到finally。

 

第十四章 类型信息

1.类字面常量
就是static final先于static代码块。 

2.泛化的class引用
newInstance()也是用来建立新的对象,其与new()的区别是:
newInstance()是弱类型,效率低,只能调用无参构造;new()是强类型,高效率,能调用任何public构造器 

FancyToy fancytoy=ftClass.newInstance();
Class<? super FancyToy> up = ftClass.getSuperclass();
//下面这个会报错
Class<Toy> up2 = ftClass.getSuperclass();
//Toy在编译期就知道它是什么类型了,即Toy.class。getSuperclass()不只仅只是“某个类,它是FancyToy”
的超类。

 

3.新的转型语法

class Building{}
class House extends Building{}
public class ClassCasts{
  public static void main(String[] args){
        Building b=new House();
        Class<House> houseType = House.class;
        House h=houseType.cast(b);//至关于h=(House)b;
  }
}

 

4.类型转换前先作检查
它的做用是用来判断,instanceof 左边对象是否为instanceof 右边类的实例。如x instanceof Dog 判断对象x是否属于Dog类。instanceof只可将其与命名类型进行比较,而不能与Class对象做比较。也可判断子类对象是否属于父类。

5.instanceof与Class的等价性
instanceof保持了类的概念,它指的是“你是这个类吗,或者你是这个类的派生类吗?”而若是用==比较实际的Class对象,就没有考虑继承——它或者是这个确切的类型,或者不是。

6.类方法提取器
getFields、getConstructors、getMethods获取的分别是一个class里面的public 变量、构造器、方法public Method[] getMethods()返回某个类的全部公用(public)方法包括其继承类的公用方法,固然也包括它所实现接口的方法。(包括继承的public)public Method[] getDeclaredMethods()对象表示的类或接口声明的全部方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。固然也包括它所实现接口的方法。(不包括继承的全部)

 


第十五章 泛型

1.泛型:

参数化类型。这个术语的意思是适用许多许多类型和接口的区别就是,不用必定要接口定义的方法。

2.理解了程序边界所在,你才能成为程序高手,由于只有知道了某个技术不能作到什么,你才能更好地作到所能作的(部分缘由是,没必要浪费时间在死胡同里乱转)。

3.泛型的主要目的之一就是用来指定容器要持有什么类型的对象,并且由编译器来保证类型的正确性。T就是类型参数。

4.泛型中能存入指定类型或者指定类型的子类,泛型与多态不冲突。

5.元组:

它是将一组对象直接打包存储于其中的一个单一对象,这个对象容许读取其中元素,可是不容许向其中存放新的对象(这个概念也称为数据传送对象或信使)。

6.final,只要存在一次等于,就不许出现第二个等于,相同都不行。在对象被构造出来以后,声明为final的元素便不能被在赋予其余值

下面这个程序,你可使用first和second,但你却没法将其余值赋给它们

public class TwoTuple<A,B> {
	public final A first;
	public final B second;
	public TwoTuple(A a,B b){
		first=a;
		second=b;
	}
}

 

7.泛型方法:
public <T> void  f(T x){ }
注意:当使用泛型类时,必须在建立对象的时候指定类型参数的值,而使用泛型方法的时候,一般没必要指明参数类型,由于编译器会为咱们找出具体的类型。这一般称为类型参数推断。

 

第十九章  枚举

1.enum:定义常量,至关于public static final。
2.除了不能继承一个enum以外,咱们基本上能够将enum看作一个常规的类。若是打算定义本身的方法,那么必须在enum实例序列的最后添加一个分号,同时必须先定义enum实例,即方法写在后面。感受也至关于通配符的意义,便于代码清晰与维护。
3.enum里面不能有重复,至关于set,可是不能存取。
4.驱动代码:就是enum中的实例对象还能够写方法,如enum a{WINKEN{syso}}}。
5.用于多路分发:就是如Number.plus(Number),或者match(new Item,new Item),Number是超类,这种不知道具体类型,多态能够动态绑定一个,但另外一个就不行,这只是引出枚举的分发。

 

第二十一章 并发

1.并发的多面性:
用并发解决的问题大致上能够分为“速度”和“设计可管理性”两种。速度:若是想让程序运行的很快,能够将其断开为多个片断,在单独的处理器上运行每一个片断。使用并发编程最吸引人的一个缘由就是要产生具备可响应的用户界面。

java的线程机制是抢占式的,这表示调度机制会周期的中断线程,将上下文切换到另外一个线程,从而为每一个线程都提供时间片,使得每一个线程都会分配到数量合理的时间去驱动它的任务。

建议使用runnable接口,由于单继承,继承thread过于局限,接口还能够继承。

start()方法被用来启动新建立的线程,并且start()内部 调用了run()方法,这和直接调用run()方法的效果不同。当你调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程。

2.基本线程机制
一个线程就是在进程中的一个单一的顺序控制流。

yield()(让步):使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程仍是有可能会被再次执行到的,并非说必定会执行其余线程而该线程在下一次中不会执行到了。

当Runnable导出一个类时,它必须具备run()方法,可是这个方法并没有特殊之处,他不会产生任何内在的线程能力,要实现线程行为,你必须显示地将一个任务附着到线程上。

Executor(执行器):单个的Executor被用来建立和管理系统中的全部任务。(就是调用线程池)

ExecutorService exec=Executors.newCachedThreadPool();
exec.execute(new Liftoff());//Liftoff()是一个线程
exec.shutdown();

newCachedThreadPool:建立一个可缓存的线程池,如有需求它会回收空闲的线程进行使用,若是没有空闲的线程,则会建立新的线程。

newFixedThreadPool:建立一个固定值的线程池,若是业务超出线程数量,那就排队,注意排队的时候是谁的线程结束了直接拿过来使用,可是线程充足的状况下,它会直接去拿新的线程。

newScheduledThreadPool:是一个建立固定长度的线程池,支持定时和周期性执行。

ScheduledExecutorService cachedThreadPool = Executors.newScheduledThreadPool(10);
for(int i=0;i<10;i++){
    cachedThreadPool.scheduleAtFixedRate(new MyThread(i), 1,1000,TimeUnit.MILLISECONDS);
    //第二个参数表示线程第一次运行的初始时间,第三个参数是下一次间隔多长时间,第四个参数是时间单位:分、秒、时等
    cachedThreadPool.schedule(new MyThread(i), 1000,TimeUnit.MILLISECONDS);
    //第二个参数就是定时,跟上面第二个参数含义同样,只是它只执行一次
}

newSingleThreadExecutor:是建立一个单线程的线程池,线程数量为1,若是多个线程时,会排队,一个线程执行完再下一个,他全部的任务都是用这个线程来执行的,保证全部任务按照指定顺序执行。他还能保证当一个线程发生异常时,他会继续往下执行。

实现线程的方式

(1)继承Thread类建立线程

(2)实现Runnable接口建立线程

(3)实现Callable接口经过FutureTask包装器来建立Thread线程

public class NewTest {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();//实现Callable接口的类
        FutureTask<Integer> futureTask = new FutureTask<Integer>(task);//将task由FutureTask包装器包装
        executor.submit(futureTask);//执行
        executor.shutdown();
        try {
            System.out.println("Task运行结果"+futureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}
class Task implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for(int i=0;i<100;i++)
            sum += i;
        return sum;
    }
}

(4)使用ExecutorService+Callable+Future实现有返回结果的线程

public class NewTest {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();//实现Callable接口的类
        Future<Integer> future = executor.submit(task);//经过executor执行,返回值类型为Future
        executor.shutdown();
        try {
            System.out.println("Task运行结果"+future.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}
class Task implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for(int i=0;i<100;i++)
            sum += i;
        return sum;
    }
}

FutureTask与Future区别:

Future能够获得Callable的返回值。

FutureTask既能够获得Callable的返回值,又能够做为Runnable被线程执行。

3.后台线程
是指在程序运行的时候在后台提供一种通用服务的线程,而且这种线程并不属于程序中不可或缺的部分。daemon.setDaemon(true)。

4.加入一个线程
若是某个线程在另外一个线程t上调用t.join(),此线程将被挂起,直到目标线程t结束才恢复(即t.isAlive()返回为假)也能够在调用join()时带上一个超时参数,这样若是目标线程在这段时间到期时尚未结束的话,join()方法总能返回。 对join()方法的调用能够被中断,作法是在调用线程上调用interrupt()方法,这时须要用到try-catch子句。

5.捕获异常
会存在异常在try catch捕获不到的状况未捕获的异常经过uncaughtException来捕获。

6.解决共享资源
锁 synchronized  一个任务得到锁,外面的任务等待锁,但不是排队,而是根据抢占式,都有机会。

7.临界区
防止多个线程同时访问方法内部的部分代码而不是防止整个方法。经过这种方式分离出来的代码段被称为临界区。这也被称为同步控制块。


8.线程本地存储
防止任务在共享资源上产生冲突的第二种方式是根除对变量的共享(第一种应该是锁,对方法的共享)。ThreadLocal对象一般当作静态域存储,它保证不会出现竞争条件。

ThreadLocal 是线程的局部变量, 是每个线程所单独持有的,其余线程不能对其进行访问, 一般是类中的 private static 字段,是对该字段初始值的一个拷贝,它们但愿将状态与某一个线程(例如,用户 ID 或事务 ID)相关联。

当使用ThreadLocal维护变量的时候 为每个使用该变量的线程提供一个独立的变量副本,即每一个线程内部都会有一个该变量,这样同时多个线程访问该变量并不会彼此相互影响,所以他们使用的都是本身从内存中拷贝过来的变量的副本, 这样就不存在线程安全问题,也不会影响程序的执行性能。

可是要注意,虽然ThreadLocal可以解决上面说的问题,可是因为在每一个线程中都建立了副本,因此要考虑它对资源的消耗,好比内存的占用会比不使用ThreadLocal要大。
(1)ThreadLocal.get: 获取ThreadLocal中当前线程共享变量的值。
(2)ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值。
(3)ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
(4)ThreadLocal.initialValue: ThreadLocal中的一个方法,默认返回null,重写此方法,可设置初始值。

9.装饰性花园
TimeUnit.SECONDS.sleep(5)线程等待五秒,TimeUnit.MILLISECONDS.sleep(5000)线程等待五秒。二者的时间单位不同。内部都是Thread.sleep实现

volatile,这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取主存即内存,其做用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。只要计算机在运行中,CPU就会把须要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来,内存的运行也决定了计算机的稳定运行。

volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。并且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任什么时候刻,两个不一样的线程老是看到某个成员变量的同一个值。(至关于每一个线程访问的是最新的变量)(这个是公用,threadlocal是各自用各自复制的副本,private。本质区别就是,threadlocal是各自用各自,这个是共用,就是用的是最新的,我加1后,你获得的是加1后的)。

10.线程状态
一个线程能够出于如下四种状态之一
(1)新建:当线程被建立时,它只会短暂的处于这种状态,此时他已经分配了必要的系统资源,并执行了初始化。此刻线程已经有资格得到cpu时间,以后调度器将把这个线程转变为可运行状态或阻塞状态。
(2)就绪(感受而后是运行)
(3)阻塞:一个任务进入阻塞状态,可能有以下缘由:
           1.经过调用sleep使线程进入休眠状态。
           2.调用wait(),join使线程挂起。
           3.任务在等待某个输入/输出完成。
           4.任务试图在某个对象上调用其同步控制方法,可是对象锁不可用(就是等待锁)。
(4)死亡

sleep与wait的区别:sleep使任务进入休眠状态,任务在指定的时间内不会运行,休眠时不释放锁。wait使线程挂起,直到线程获得了notify或notifyAll消息,线程才会进入就绪状态,挂起时释放资源锁。

为何sleep属于Thread,wait属于Object?
其实二者均可以让线程暂停一段时间,可是本质的区别是一个线程的运行状态控制,一个是线程之间的通信的问题。就是说sleep是线程里,wait是线程之间。

11.中断:用来打断被阻塞的任务
你能中断对sleep的调用,可是,你不能中断正在试图获取synchronized锁或者试图执行i/o操做的线程。它的两个例子,一个用的socket的close,另外一个是nio通道,nio通道就能够中断i/o操做。

注意,当你在线程上调用interrupt()时,中断发生的惟一时刻是在任务要进入阻塞操做中,或者已经在阻塞操做内部时。

interrupted:检查中断状态,这不只能够告诉你interrupt()是否被调用过,并且还能够清除中断状态。

interrupt() 向当前调用者线程发出中断信号
isinterrupted() 查看当前中断信号是true仍是false
interrupted() 是静态方法,查看当前中断信号是true仍是false而且清除中断信号,顾名思义interrupted为已经处理中断信号。

另外interruptedException时,他会清除掉interrupt信号。

ReentrantLock上阻塞的任务具有能够被中断的能力。

12.线程之间的协做
调用sleep或者yield的时候,锁并无被释放。当一个任务在方法里遇到了对wait的调用的时候,线程的执行被挂起,对象上的锁被释放。

调用wait时就是在声明:我已经刚刚作完能作的全部事情,所以我要在这里等待,可是我但愿其余的synchronized操做在条件适合的状况下可以执行。 其实就是说我作完了一部分工做,要等别人的工做完成,我才能继续工做,而后释放工具,但愿其余须要而人能继续工做。

能够经过notify、notifyAll、或者时间到期,从wait中恢复执行。

13.
notify:唤醒在此对象监视器上等待的单个线程。若是全部线程都在此对象上等待,必须等待相同的条件,将只唤醒等待特定锁的任务。则会选择唤醒其中一个线程。选择是任意性的,并在对实现作出决定时发生。notifyall:唤醒在此对象监视器上等待的全部线程。

错失的信号:就是抢占式的时候,线程先调用了notify的线程,后调用的wait线程,形成无限等待的死锁。


14.死锁
当如下四个条件同时知足时,就会发生死锁:
1.互斥条件。任务使用的资源中至少有一个是不能共享的。
2.至少有一个任务它必须持有一个资源且正在等待获取一个当前被识别的任务持有的资源。
3.资源不能被抢占,任务必须把资源释放看成普通事件。
4.必须有循环等待。这时,一个任务等待其余任务所持有的资源,后者又在等待另外一个任务所持有的资源,这样一直下去,直到有一个任务在等待第一个任务所持有的资源,使得你们被锁住。

死锁必须这四个条件同时知足,因此要解决死锁,只需破坏其中一个条件,最好破坏4。