线程的概念与创建

来源:互联网 发布:圣劳伦斯大学知乎 编辑:程序博客网 时间:2024/06/13 23:16

线程的综述

       线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机;运行状态是指线程占有处理机正在运行;阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。

       线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。

与进程比较:
       进程是资源分配的基本单位。所有与该进程有关的资源,都被记录在进程控制块PCB中。以表示该进程拥有这些资源或正在使用它们。另外,进程也是抢占处理机的调度单位,它拥有一个完整的虚拟地址空间。当进程发生调度时,不同的进程拥有不同的虚拟地址空间,而同一进程内的不同线程共享同一地址空间。与进程相对应,线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。线程只由相关堆栈(系统栈或用户栈)寄存器和线程控制表TCB组成。寄存器可被用来存储线程内的局部变量,但不能存储其他线程的相关变量。通常在一个进程中可以包含若干个线程,它们可以利用进程所拥有的资源。在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系统资源的利用率和吞吐量。

线程与进程的区别可以归纳为以下4点:

  1. 地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
  2. 通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要进程同步和互斥手段的辅助,以保证数据的一致性。
  3. 调度和切换:线程上下文切换比进程上下文切换要快得多。
  4. 在多线程OS中,进程不是一个可执行的实体。

            这里写图片描述
                这里写图片描述

三种创建方式

  • 一、继承Tread
/** * 模拟龟兔赛跑,实现多线程方法1 * 1、创建多线程 继承Thread 需要重写run(线程体)方法 相当于程序从run()方法开始 * 2、使用线程:创建子类对象  * 3、调用对象.start方法,启动线程,自己会调用内部的run方法 * @author Administrator * */public class rabbit extends Thread{    @Override    public void run() {        super.run();        for(int i=0;i<100;i++){            System.out.println("兔子跑了"+i);        }    }}class Tortoise extends Thread{    @Override    public void run() {        super.run();        for(int i=0;i<100;i++){            System.out.println("乌龟跑了"+i);        }    }}
public class Rabbitapp {    public static void main(String[] args) {        //创建子类对象        rabbit r = new rabbit();        Tortoise t = new Tortoise();        //调用start方法,启动线程,自己会调用内部的run方法        r.start();        t.start();        //r.run 如果这么使用的话就会先把一个线程执行完之后,才会去执行其他的        for(int i=0;i<100;i++){            System.out.println("main-->"+i);        }    }}
输出结果:乌龟跑了0 乌龟跑了1 兔子跑了0 main-->0         兔子跑了1 乌龟跑了2 兔子跑了2 兔子跑了3
  • 二、实现Runable接口实现多线程
/** * 继承Tread类方式实现多线程有缺点:那就是如果我们的类已经继承了一个类,比如说该类已经继承了其他小类, * 那么就无法继续继承Tread类 * 所以可以使用Runable接口实现多线程 * 优点:可以同时实现继承,避免单继承,方便共享资源,同一份资源,多个代理访问 * 静态代理 设计模式 * 1、真实角色 * 2、代理角色 * 3、二者实现相同的接口 * @author Administrator * */public class StaticProxy {    public static void main(String[] args) {        You you = new You();  //创建真实角色        WeddingCompany we = new WeddingCompany(you);  //创建代理角色+真实角色的引用        //执行任务        we.marry();    }}//接口interface Marry{    void marry();}//真实角色class You implements Marry{    @Override    public void marry() {        System.out.println("结婚了");    }}//代理角色-->持有真实角色的引用class WeddingCompany implements Marry{    private Marry you;    public WeddingCompany() {    }    public WeddingCompany(Marry you) {        super();        this.you = you;    }    private void before(){        System.out.println("准备新房");    }    private void after(){        System.out.println("入洞房");    }    @Override    public void marry() {        before();        you.marry();        after();    }}

下面就利用runnable实现多继承:

public class Video {    public static void main(String[] args) {        //创建真实角色        ChatQQ c = new ChatQQ();        //创建代理角色        Thread t = new Thread(c);        //调用.stat,启动线程        t.start();        for(int i=0;i<20;i++){            System.out.println("在看"+i+"集电视剧!");        }    }}class ChatQQ implements Runnable{    @Override    public void run() {        for(int i=0;i<20;i++){            System.out.println("和"+i+"个人在聊QQ!");        }    }}
public class Web12306 implements Runnable {    private int num = 50;//1到50号    @Override    public void run(){        while(true) {            if(num <= 0){                break;//跳出循环            }            System.out.println(Thread.currentThread().getName() + "抢到了" + num--);        }    }    public static void main(String[] args) {        //真实角色        Web12306 web = new Web12306();        //代理        Thread t1 = new Thread(web, "路人甲");        Thread t2 = new Thread(web, "黄牛乙");        Thread t3 = new Thread(web, "攻城狮");        t1.start();        t2.start();        t3.start();    }}
  • 三、实现Callable接口
    优点:可以获取返回值
    Callable和Future接口
        Callable是类似于Runnable的接口,实现Callable接口的类和实现Runnable接口的类都是可被其它线程执行的任务
    Callable和Runnable有几点不同:
                1、Callable规定的方法是call(),而Runnable规定的方法是run()
                2、call()方法可抛出异常,而run()方法不能抛出异常
                3、Callable的任务执行后可返回值,运行Callable任务可得到一个Future对象,而Runnable的任务不能返回值
        Future表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
    缺点:繁琐
    思路:
        1)创建:Callable实现类 +重写call
        2)借助:执行调度服务ExecutorService,获取Future对象
                    ExecutorService ser = Executors.newFixedThreadPool(2);
                    Future result = ser.submit(实现类对象)
        3)获取值:result.get()
        4)停止服务:ser.shutdownNow();
import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class Test_Callable {    public static void main(String[] args) throws InterruptedException, ExecutionException {         //创建线程        ExecutorService ser = Executors.newFixedThreadPool(2);//2个线程        Race gui = new Race("乌龟",1000);        Race tutu = new Race("小兔子",100);        //获取值        Future<Integer> result1 = ser.submit(gui);        Future<Integer> result2 = ser.submit(tutu);        Thread.sleep(2000);        gui.setFlag(false);        tutu.setFlag(false);        int num1 = result1.get();        int num2 = result2.get();        System.out.println("乌龟跑了-->>" + num1 + "步");        System.out.println("小兔子跑了-->>" + num2 + "步");        //停止服务        ser.shutdownNow();    }}class Race implements Callable<Integer>{    private String name;  //名称    private long time;  //延时    private boolean flag=true;    private int step;  //步数    public Race() {    }    public Race(String name) {        super();        this.name = name;    }    public Race(String name, long time) {        super();        this.name = name;        this.time = time;    }    public Race(String name, long time, boolean flag, int step) {        super();        this.name = name;        this.time = time;        this.flag = flag;        this.step = step;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public long getTime() {        return time;    }    public void setTime(long time) {        this.time = time;    }    public boolean isFlag() {        return flag;    }    public void setFlag(boolean flag) {        this.flag = flag;    }    public int getStep() {        return step;    }    public void setStep(int step) {        this.step = step;    }    @Override    public Integer call() throws Exception {        while(flag){            Thread.sleep(time);            step++;        }        return step;    }}