Java多线程-Thread、Runnable、Executor

一、线程与进程
进程是指处于运行过程中的程序,并且具有一定的独立功能。进程是系统进行资源分配和调度的一个单位。当程序进入内存运行时,即为线程。

进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。

    ① 进程是系统进行资源分配的最小单位,也是系统进行资源调度的基本执行单元。
    ② 它是程序的一次执行过程。简单点的说“进程是正在运行的程序的实例”。
    ③ 每个进程运行在受保护的独立的内存空间内,进程和进程之间互不干扰。

线程:线程是进程中的一个实体,是被系统独立调度和分派的基本单位。

  ① 线程是CPU调度的最小单位,是进程中的一个实体。
    ② 每一个应用程序在启动之后,都会默认开启一条主线程,除了主线程,其他的线程都是子线程。

    ③ 一个线程可以创建和撤销另一个线程。

线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
  多进程是指操作系统能同时运行多个任务(程序)。

  多线程是指在同一程序中有多个顺序流在执行。

并行与并发:
并行:多个cpu实例或者多台机器同时执行一段处理逻辑,是真正的同时。
并发:通过cpu调度算法,让用户看上去同时执行,实际上从cpu操作层面不是真正的同时。

二、线程的前两种实现-Thread&Runable

API:

Thread类:  public class Thread extends Object implements Runnable

Runnable接口:public interface Runnable

可见,Thread类是Runnable接口的一个实现类。

线程的生命周期:

创建:新建一个线程对象,如Thread thd=new Thread()。

就绪:创建了线程对象后,调用了线程的start()方法(此时线程只是进入了线程队列,等待获取CPU服务 ,具备了运行的条件,但并不一定已经开始运行了)。

运行:处于就绪状态的线程,一旦获取了CPU资源,便进入到运行状态,开始执行run()方法里面的逻辑。
终止:线程的run()方法执行完毕,或者线程调用了stop()方法,线程便进入终止状态。
阻塞:一个正在执行的线程在某些情况时,由于某种原因而暂时让出了CPU资源,暂停了自己的执行,便进入了阻塞状态,如调用了sleep()方法。

(一)Thread类
class MyThread extends Thread{  
    private int ticket = 5;  
    public void run(){  
        for(int i = 0;i<100;i++){  
            if(ticket>0){//判断是否还有剩余票  
                System.out.println("卖票,ticket = "+ticket--);  
            }  
        }  
    }  
};  
public class ThreadDemo04{  
    public static void main(String args[]){  
        MyThread mt1 = new MyThread();  
        MyThread mt2 = new MyThread();  
        MyThread mt3 = new MyThread();  
        mt1.start();//调用线程主体让其运行  
        mt2.start();//三个地方同时卖票  
        mt3.start();  
    }  
};    
(二)Runnable接口
class MyThread implements Runnable{  
    private int ticket=5;  
    public void run(){  
        for(int i = 0;i<100;i++){  
            if(ticket>0){  
                System.out.println("卖票,ticket = "+ticket--);  
            }  
        }  
    }  
};  
public class RunnableDemo02{  
    public static void main(String args[]){  
        MyThread my1 = new MyThread();  
        new Thread(my1).start(); //启动三个线程  
        new Thread(my1).start(); //共享my1中资源  
        new Thread(my1).start();   
    }  
};  

run()方法是多线程程序的一个约定。所有的多线程代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。

(三)二者的区别与使用选择
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runnable接口的话,则很容易的实现资源共享。
实现Runnable接口比继承Thread类所具有的优势:
1:适合多个相同的程序代码的线程去处理同一个资源
2:可以避免java中的单继承的限制
3:增加程序的健壮性,代码可以被多个线程共享,代码和数据独立

4:线程池只能放入实现Runnable或callable类线程,不能直接放入继承Thread的类

三、线程的第三种实现-Executor线程池框架
(一)Why
new Thread()的缺点
1、每次new Thread()耗费性能。
2、调用new Thread()创建的线程缺乏管理,而且可以无限制创建,之间的竞争会导致过多占用系统资源导致系统瘫痪。
3、不利于扩展,比如如定时执行、定期执行、线程中断。

采用线程池的优点
1、重用存在的线程,减少对象创建、消亡的开销,性能佳。
2、可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
3、提供定时执行、定期执行、单线程、并发数控制等功能。
(二)Executor框架的两级调度模型
个人觉得这篇文章写的很好:点击打开链接,以下内容基本摘自该篇文章。
在HotSpot VM的线程模型中,Java线程被一对一映射为本地操作系统线程。 Java线程启动时会创建一个本地操作系统线程;当Java线程终止时,这个操作系统线程也会被回收。操作系统会调用所有线程并将他们分配给可用的CPU。
可以将此种模式分为两层,在上层,Java多线程程序通常把应用程序分解为若干任务,然后使用用户级的调度器(Executor框架)将这些任务映射为固定数量的线程;在底层,操作系统内核将这些线程映射到硬件处理器上。

Executor框架的结构
1. 任务
包括被执行任务需要实现的接口:Runnable接口和Callable接口。
2. 任务的执行
包括任务执行机制的核心接口Executor,以及继承自Executor的ExecutorService接口。
Executor框架有两个关键类实现了ExecutorService接口:ThreadPoolExecutor 和 ScheduledThreadPoolExecutor。

3. 异步计算的结果
包括Future和实现Future接口的FutureTask类。
(三)框架的使用
1、主线程首先要创建实现 Runnable接口或者Callable接口的任务对象。工具类Executors可以把一个Runnable对象封装为一个Callable对象。
Executors.callable(Runnale task);
或
Executors.callable(Runnable task, Object resule);

其中,callable结构需要实现一个call()方法:

package java.util.concurrent;  
  
public interface Callable<V> {  
    /** 
     * Computes a result, or throws an exception if unable to do so. 
     * 
     * @return computed result 
     * @throws Exception if unable to compute a result 
     */  
    V call() throws Exception;  
}

2、然后可以把Runnable对象直接交给ExecutorService执行
ExecutorServicel.execute(Runnable command);
或者也可以把Runnable对象或Callable对象提交给ExecutorService执行
ExecutorService.submit(Runnable task);
如果执行ExecutorService.submit(...),ExecutorService将返回一个实现Future接口的对象(到目前为止的JDK中,返回的是FutureTask对象)。由于FutureTask实现了Runnable接口,我们也可以创建FutureTask类,然后直接交给ExecutorService执行。
3、最后,主线程可以执行FutureTask.get()方法来等待任务执行完成。主线程也可以执行FutureTask.cancel(boolean mayInterruptIfRunning)来取消此任务的执行。

具体实现过程的代码分析,参见这篇文章:点击打开链接

(四)ThreadPoolExecutor介绍
 Executor框架最核心的类是ThreadPoolExecutor。
 ThreadPoolExecutor的组件构成
1、corePool:核心线程池的大小
2、maximumPool:最大线程池的大小
3、BlockingQueue:用来暂时保存任务的工作队列
4、RejectedExecutionHandler:当ThreadPoolExecutor已经关闭或ThreadPoolExecutor已经饱和时(达到了最大线程池的大小且工作队列已满),execute()方法将要调用的Handler。
 
Executor 可 以 创 建 3 种 类 型 的 ThreadPoolExecutor 线 程 池:
1. FixedThreadPool
2. SingleThreadExecutor
3. CachedThreadPool

参考资料: https://cloud.tencent.com/developer/article/1006991

                https://blog.csdn.net/evankaka/article/details/44153709

                https://www.cnblogs.com/study-everyday/p/6737428.html

                http://www.imooc.com/article/14377


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值