自定义线程

Java与Kotlin自定义线程全解析

每个程序至少有一个线程 —— 主线程

主线程是程序的起点,你可以从它开始创建新的线程来执行任务。为此,你需要创建自定义线程,编写在线程中执行的代码,并启动它。

通过继承创建自定义线程

创建新线程有两种主要方式:

  1. 继承 Thread 类并重写其 run 方法

  2. 实现 Runnable 接口,并将实现类传递给 Thread 类的构造函数

下面是继承 Thread 类并重写 run 方法的示例:

class HelloThread : Thread() {
    override fun run() {
        val helloMsg = "Hello, i'm $name"
        println(helloMsg)
    }
}

而下面是实现 Runnable 接口并传递给 Thread 构造函数的示例:

class HelloRunnable : Runnable {
    override fun run() {
        val threadName = Thread.currentThread().name
        val helloMsg = "Hello, i'm $threadName"
        println(helloMsg)
    }
}

这两种方式都需要你重写 run 方法,它是一个普通的方法,包含了你要执行的代码。选择哪种方式取决于具体任务和个人偏好。

如果你继承了 Thread 类,可以直接使用其字段和方法,但 Kotlin 不支持多重继承,所以你不能再继承其他类。

创建线程对象

Thread 类有很多构造函数,我们来看其中几个示例:

val t1 = HelloThread()              // 使用子类
val t2 = Thread(HelloRunnable())    // 传入 Runnable 实现
val myThread = Thread(HelloRunnable(), "my-thread") // 指定线程名

如果你想在 HelloThread 类中设置线程名,需要重写构造函数。

因此,实现 Runnable 接口是一种更灵活的方式。

使用 Lambda 更简单

如果你已经熟悉 Lambda 表达式,可以用下面这种方式:

val t3 = Thread {
    println("Hello, i'm ${Thread.currentThread().name}")
}

更简便的线程创建方式

如果不想继承或实现接口,也可以使用 thread(...) 函数创建线程,它来自 kotlin.concurrent 包:

import kotlin.concurrent.thread

val t4 = thread(start = false, name = "Thread 4", block = {
    println("Hello, I'm ${Thread.currentThread().name}")
})

该函数的参数包括:

  • start:是否立即启动线程;

  • isDaemon:是否为守护线程;

  • contextClassLoader:类加载器;

  • name:线程名称;

  • priority:优先级;

  • block:线程执行代码块。

启动线程

使用 Thread 类的 start() 方法启动线程。调用后,run 方法会在新线程中自动执行,但不会立刻执行

示例:

fun main() {
    val t = thread(start = false, block = {
        println("Hello, I'm ${Thread.currentThread().name}")
    })
    t.start()
}

或者直接设置 start = true(默认值):

fun main() {
    val t = thread(block = {
        println("Hello, I'm ${Thread.currentThread().name}")
    })
}

输出示例:

Hello, i'm Thread-0

启动线程的内部原理

线程启动不会立即执行,启动与运行之间存在延迟。线程默认是非守护线程

  • 守护线程不会阻止 JVM 退出;

  • 非守护线程还在运行时,JVM 不会退出;

  • 守护线程通常用于后台任务。

注意

  • start() 会启动新线程并执行 run() 方法;

  • 直接调用 run() 不会启动新线程,只是在当前线程中执行;

  • 多次调用 start() 会抛出 IllegalThreadStateException

  • 多线程中的执行顺序是不可预测的,除非使用了额外的同步机制。

示例代码:

fun main() {
    val t1 = HelloThread()
    val t2 = HelloThread()
    t1.start()
    t2.start()

    println("Finished")
}

可能的输出顺序:

Hello, i'm Thread-1
Finished
Hello, i'm Thread-0

也可能是:

Finished
Hello, i'm Thread-0
Hello, i'm Thread-1

说明线程的启动顺序与实际运行顺序可能不同。

一个简单的多线程程序

下面是一个简单的多线程示例:

一个线程不断读取用户输入并打印其平方,主线程则间歇性输出消息。

工作线程类:

class SquareWorkerThread(name: String) : Thread(name) {
    override fun run() {
        while (true) {
            val number = readln().toInt()
            if (number == 0) {
                break
            }
            println(number * number)
        }
        println("$name's finished")
    }
}

主线程逻辑:

fun main() {
    val workerThread = SquareWorkerThread("square-worker")
    workerThread.start()

    for (i in 0 until 5_555_555_543L) {
        if (i % 1_000_000_000 == 0L) {
            println("Hello from the main!")
        }
    }
}

示例输入与输出(带注释):

Hello from the main!    // 主线程输出
2                       // 输入
4                       // 输出平方
Hello from the main!    // 主线程输出
3
9
5
Hello from the main!
25
0                       // 输入 0 终止线程
square-worker finished  // 工作线程结束提示
Hello from the main!
Hello from the main!

最终输出表明,两个线程并发执行。虽然不是真正同时,但每个线程都得到了执行的机会。


总结

  • 如何创建线程对象;

  • 如何设置线程执行的代码;

  • 如何启动线程;

  • 线程背后的工作原理;

  • 线程运行的非确定性;

  • 简单的多线程程序如何写。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值