Java多线程——Thread与Runnable

本文介绍了Java多线程中的Thread类和Runnable接口的实现方式,包括进程与线程的基本概念。建议使用实现Runnable接口的方式创建线程,因为它可以避免Java的单继承限制,并且适合多个线程共享同一资源。文章还对比了扩展Thread类和实现Runnable接口在多线程情况下的差异,强调在实际开发中实现Runnable接口更优。

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

Java多线程——Thread与Runnable

一、进程与线程

进程——进程是系统资源分配、调度的基本单位,它可以申请和占用系统资源,每一个进程都有属于自己的地址空间,各进程相互独立。进程是程序的执行实体,程序本身是一个静态的概念,只有当程序执行时它才成为一个活动的实体,也就是进程。进程是线程的容器。

线程——线程是是程序执行的最小单元,它是进程的一个实体,是被系统调度和分派CPU的基本单位。线程自己不拥有系统资源(除自身运行的必须资源),同一个进程中的所有线程共享进程拥有的全部资源。

由于进程与线程的各自特点,所以进程通信比线程通信要困难的多。

二、Java多线程的实现方式

Java中对于多线程的实现方式主要有两种:扩展Thread类本身、实现Runnable接口。建议采用实现Runnable接口的方式。

2.1 扩展Thread类的实现方式

Thread常用方法:

方法含义
getName()获取线程名称
getPriority()获取线程优先级
isAlive()确认线程是否仍然在运行
join()等待线程终止
run()线程的入口
sleep()挂起线程一段时间
start()通过调用线程的run()方法启用线程

public class TicketerThreadExt extends Thread {
    private Integer tickes;

    public TicketerThreadExt(Integer tickes) {
        this.tickes = tickes;
    }

    @Override
    public void run() {
        System.out.println("启动线程:开始售票");
        System.out.println("线程名:" + Thread.currentThread().getName());
        super.run();
        while (tickes > 0) {
            System.out.println("剩余票数: " + (--tickes));
        }
        System.out.println("车票售罄:终止线程");

    }

    public static void main(String[] args) {
        TicketerThreadExt ticker = new TicketerThreadExt(5);
        // 设置线程名
        ticker.setName("Ticketer");
        ticker.start();
    }
}

执行结果:

启动线程:开始售票
线程名:Ticketer    
剩余票数: 4
剩余票数: 3
剩余票数: 2
剩余票数: 1
剩余票数: 0
车票售罄:终止线程

2.2 扩展Runnable接口的实现方式

继承Runnable接口时,只需要实现run()方法就可以,在run()方法中可以调用其他方法、使用其他的类、也可以声明变量,它为并发线程的执行建立了入口。

// 售票员线程
public class TicketerThreadImpl implements Runnable {
    private Integer tickets;

    public TicketerThreadImpl(Integer tickets) {
        this.tickets = tickets;
    }

    // 线程入口
    public void run() {
        System.out.println("启动线程:开始售票");
         System.out.println("线程名:" + Thread.currentThread().getName());
        // 售票操作
        while (tickets > 0) {
            System.out.println("剩余票数" + (--tickets));
        }
        System.out.println("车票售罄:退出线程");
    }

    public static void main(String[] args) {
        TicketerThreadImpl ticker = new TicketerThreadImpl(5);
        // 创建新线程,第二个参数为指定的线程名
        Thread tickerWindow = new Thread(ticker, "Ticketer");
        tickerWindow.start();
    }
}

执行结果:

启动线程:开始售票
线程名:Ticketer
剩余票数4
剩余票数3
剩余票数2
剩余票数1
剩余票数0
车票售罄:退出线程

三、扩展Thread类与实现Runnable接口的比较(多线程情况)

  • 实现Runnable方式可以避免扩展hread方式由于Java单继承特性的缺点
  • 实现Runnable的代码可以被多个线程共享,适合多个线程处理同一资源的情况
  • 扩展Thread可以通过某些方式增强或修改类的功能

实际开发中,创建子线程的最好方式是实现Runnable接口

下面对两种方式在多线程情况下的表现做下比较

3.1继承Thread的方式

public class TicketerExt extends Thread {

    private Integer tickets;

    public TicketerExt(String threadName) {
        super(threadName);
        this.tickets = 5;
    }

    @Override
    public void run() {
        System.out.println("启动线程:开始售票:线程名" + Thread.currentThread().getName());
        super.run();
        while (tickets > 0) {
            System.out.println(Thread.currentThread().getName() + "售出1张,剩余票数:" + (--tickets));
        }
        System.out.println("车票售罄:终止线程" + Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        TicketerExt ticker1 = new TicketerExt("Ticker1");
        TicketerExt ticker2 = new TicketerExt("Ticker2");
        TicketerExt ticker3 = new TicketerExt("Ticker3");

        ticker1.start();
        ticker2.start();
        ticker3.start();

    }
}

执行结果:

启动线程:开始售票:线程名Ticker1
启动线程:开始售票:线程名Ticker3
启动线程:开始售票:线程名Ticker2
Ticker3售出1张,剩余票数:4
Ticker1售出1张,剩余票数:4
Ticker3售出1张,剩余票数:3
Ticker2售出1张,剩余票数:4
Ticker3售出1张,剩余票数:2
Ticker1售出1张,剩余票数:3
Ticker3售出1张,剩余票数:1
Ticker3售出1张,剩余票数:0
车票售罄:终止线程Ticker3
Ticker2售出1张,剩余票数:3
Ticker1售出1张,剩余票数:2
Ticker1售出1张,剩余票数:1
Ticker1售出1张,剩余票数:0
车票售罄:终止线程Ticker1
Ticker2售出1张,剩余票数:2
Ticker2售出1张,剩余票数:1
Ticker2售出1张,剩余票数:0
车票售罄:终止线程Ticker2

3.2、实现Runnable的方式


/**
 * @author cyrus
 * @version 1.0
 * @desc description
 * @date Create in 2018/01/30 15:47
 */
public class TicketerImpl implements Runnable {
    private Integer tickets;

    public TicketerImpl() {
        this.tickets = 10;
    }

    // 注意在实际应用中,要对操作同一资源的执行块进行加锁,这里仅作对比用
    public void run() {
        System.out.println("启动线程:开始售票:线程名:" + Thread.currentThread().getName());
        while (tickets > 0) {
            System.out.println(Thread.currentThread().getName() + "售出1张票,剩余票数:" + (--tickets));
        }
        System.out.println("车票售罄:终止线程" + Thread.currentThread().getName());
    }


    public static void main(String[] args) {
        TicketerImpl ticketer = new TicketerImpl();
        Thread ticketer1 = new Thread(ticketer,"ticketer1");
        Thread ticketer2 = new Thread(ticketer,"ticketer2");
        Thread ticketer3 = new Thread(ticketer,"ticketer3");
        ticketer1.start();
        ticketer2.start();
        ticketer3.start();

    }
}

执行结果如下:

启动线程:开始售票:线程名:ticketer2
启动线程:开始售票:线程名:ticketer3
启动线程:开始售票:线程名:ticketer1
ticketer3售出1张票,剩余票数:8
ticketer2售出1张票,剩余票数:9
ticketer3售出1张票,剩余票数:6
ticketer1售出1张票,剩余票数:7
ticketer3售出1张票,剩余票数:4
ticketer2售出1张票,剩余票数:5
ticketer3售出1张票,剩余票数:2
ticketer1售出1张票,剩余票数:3
ticketer3售出1张票,剩余票数:0
ticketer2售出1张票,剩余票数:1
车票售罄:终止线程ticketer3
车票售罄:终止线程ticketer1
车票售罄:终止线程ticketer2

注意:

输出的结果不是按序输出,是因为线程在操作玩同一资源tickets后,即释放CPU资源,在之后的过程中才开始打印操作,在此过程中,CPU资源可能被其他线程抢占,因此才造成输出结果是乱序。这里仅仅是用作对比继承thread方式,所以没有进行加锁,实际操作中需要对抢占式资源进行互斥访问。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值