线程的概念与创建

本文介绍了线程的概念、特点及与进程的区别,并详细讲解了Java中创建线程的三种方式:继承Thread类、实现Runnable接口及实现Callable接口。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线程的综述

       线程,有时被称为轻量级进程(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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值