进程包含如下三个特征。
    > 独立性:进程是系统中独立存在的实体,它可以拥有自己独立的资源,每一个进程都拥有自己私
    有的地址空间。在没有经过进程本身允许的情况下,一个用户进程不可以直接访问其他进程的地
    址空间。
    > 动态性:进程与程序的区别在于,程序只是一个静态的指令集合,而进程是一个正在系统中活
    动的指令集合。在进程中加入了时间的概念。进程具有自己的生命周期和各种不同的状态,这
    些概念在程序中都是不具备的。
    > 并发性:多个进程可以在单个处理器上并发执行,多个进程之间不会互相影响。

    注意 :

    并发性(concurrency)和并行性(parallel)是两个概念,并行指在同一时刻,有多条
    指令在多个处理器上同时执行;并发指在同一时刻只能有一条指令执行,但多个进程指令
    被快速轮换执行,使得在宏观上具有多个进程同时执行的效果。

    创建线程的三种方式对比
    通过继承 Thread 类或实现 Runnable、Callable 接口都可以实现多线程,不过实现 Runnable 接口与实现 Callable 接口的方式基本相同,只是 Callable 接口里定义的方法有返回值,可以声明抛出异常而已。因此可以将实现 Runnable 接口和实现 Callable 接口归为一种方式。这种方式与继承 Thread 方式之间的主要差别如下。

    采用实现 Runnable、Callable 接口的方式创建多线程的优缺点:
    > 线程类只是实现了 Runnable 接口或 Callable 接口,还可以继承其他类。
    在这种方式下,多个线程可以共享同一个 target 对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将 CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
    > 劣势是,编程稍稍复杂,如果需要访问当前线程,则必须使用 Thread.currentThread()方法。
    采用继承 Thread 类的方式创建多线程的优缺点:
    > 劣势是,因为线程类已经继承了 Thread 类,所以不能再继承其他父类。
    >优势是,编写简单,如果需要访问当前线程,则无须使用Thread.currentThread()方法,直接使用this 即可获得当前线程。
    鉴于上面分析,因此一般推荐采用实现 Runnable 接口、Callable 接口的方式来创建多线程。

    线程生命周期
    多线程 - 图1
    sleep()
    如果希望调用子线程的 start()方法后子线程立即开始执行,程序可以使用 Thread.sleep(1)来让当前运行的线程(主线程)睡眠1 毫秒 ,1 毫秒就够了,因为在这 1 毫秒内 CPU不会空闲,它会去执行另一个处于就绪状态的线程,这样就可以让子线程立即开始执行。

    线程阻塞
    所有现代的桌面和服务器操作系统都采用抢占式调度策略,但一些小型设备如手机则可能采用协作式调度策略,在这样的系统中,只有当一个线程调用了它的 sleep()或 yield()方法后才会放弃所占用的资源 — 也就是必须由该线程主动放弃所占用的资源。
    当发生如下情况时,线程将会进入阻塞状态。
    > 线程调用 sleep()方法主动放弃所占用的处理器资源。
    > 线程调用了一个阻塞式 IO方法,在该方法返回之前,该线程被阻塞。
    > 线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有。关于同步监视器的知识、后面将有更深入的介绍。
    > 线程在等待某个通知(notify)。
    >程序调用了线程的 suspend()方法将该线程挂起。但这个方法容易导致死锁,所以应该尽量避免使用该方法。

    解除阻塞
    针对上面几种情况,当发生如下特定的情况时可以解除上面的阻塞,让该线程重新进入就绪状态。
    > 调用 sleep()方法的线程经过了指定时间。
    >线程调用的阻塞式 IO方法已经返回。
    >线程成功地获得了试图取得的同步监视器。
    >线程正在等待某个通知时,其他线程发出了一个通知。
    >处于挂起状态的线程被调用了 resume()恢复方法。

    线程死亡
    线程会以如下三种方式结束,结束后就处于死亡状态。
    > run()或 call()方法执行完成,线程正常结束。
    > 线程抛出一个未捕获的 Exception 或 Error。
    > 直接调用该线程的 stop()方法来结束该线程,该方法容易导致死锁,通常不推荐使用。

    sleep()方法和 yield()方法的区别
    > sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级;
    但 yield()方法只会给优先级相同,或优先级更高的线程执行机会。
    >sleep()方法会将线程转入阻塞状态,直到经过阻塞时间才会转入就绪状态;
    而 yield()不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。因此完全有可能某个线程调用 yield()方法暂停之后,立即再次获得处理器资源被执行。
    > sleep()方法声明抛出了 InterruptedException 异常,所以调用 sleep()方法时要么捕捉该异常,要么显式声明抛出该异常;
    而 yield()法则没有声明抛出任何异常。
    > sleep()方法比 yield()方法有更好的可移植性,通常不建议使用 yield()方法来控制并发线程的执行。

    线程同步
    1.同步代码块

    1. synchronize(obj){
    2. ...
    3. //此处代码就是同步代码块
    4. }

    2.同步方法

    1. public synchronize void draw(){
    2. ...
    3. //同步方法
    4. }

    3.同步锁

    1. class X{
    2. // 定义锁对象
    3. private final ReentrantLock lock = new ReentrantLock () ;
    4. public void m (){// 定义需要保证线程安全的方法
    5. lock. lock () ;// 加锁
    6. try{
    7. // 需要保证线程安全的代码
    8. // ... method body
    9. }
    10. finally{// 使用 finally 块来保证释放锁
    11. lock. unlock () ;
    12. }
    13. }
    14. }

    Java 线程池
    Java通过Executors提供四种线程池,分别为:
    newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
    newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
    newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO,优先级)执行。