线程基础知识

线程基础知识

1. 创建线程常用的三种方式

  1. 继承Trhead类

/**
 * @since 2022/9/8 19:26
 * @author monk
 */
class MyThread :Thread(){
    override fun run() {
        println("当前线程:${currentThread().toString()}")
    }
}

fun main() {
    val myThread = MyThread()
    myThread.start()

    println("当前线程:${Thread.currentThread().toString()}")
}

/**
* 结果:
* 当前线程:Thread[main,5,main]
* 当前线程:Thread[Thread-0,5,main]
*/
  1. 实现Runnable接口
class MyRunnable :Runnable{
    override fun run() {
        println("子线程:${Thread.currentThread()}")
    }
}

fun main() {
    val myRunnable = MyRunnable()
    Thread(myRunnable).start()

    println("主线程:${Thread.currentThread()}")
}

/**
* 结果:
* 主线程:Thread[main,5,main]
* 子线程:Thread[Thread-0,5,main]
*/
  1. 实现Callable接口
class MyCallable<String> : Callable<kotlin.String> {

    override fun call(): kotlin.String {
        return "子线程:${Thread.currentThread()}"
    }
}

fun main() {
    val myCallable = MyCallable<String>()
    val singleThread = Executors.newSingleThreadExecutor()
    val submit = singleThread.submit(myCallable)
    println(try {
        submit.get()
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        singleThread.shutdown()
    })

    println("主线程:${Thread.currentThread()}")
}

/**
* 结果:
* 子线程:Thread[pool-1-thread-1,5,main]
* 主线程:Thread[main,5,main]
*/

2. 线程的生命周期

参考《参考《深入理解Java虚拟机》Java与线程-状态转换》

Java语言定义了5中进程状态,在任意一个时间点中,一个进程只能有且只有其中一种状态,这5中状态分别是:

  1. 新建(New):创建后尚未启动的线程处于这种状态

  2. 运行(Runable):包括操作系统线程状态中的Running 和 Ready,也就是处于此状态的线程有可能正在执行,也有可能正在等待着CPU为它分配执行时间

  3. 无限期等待(Waiting):处于这种状态的进程不会被分配CPU执行时间,它们要等待被其它线程显示地唤醒,以下方法会让线程陷入无限期等待状态:

    • 没有设置Timeout参数的 wait() 方法
    • 没有设置Timeout参数的 join()方法
    • LockSupport.park()方法
  4. 限期等待(Timed Waitting):处于这种状态的进程也不会被分配CPU执行时间,不过无须等待被其他线程显示地唤醒,在一定时间之后它们会由系统自动唤醒

    • Thread.sleep()方法。
    • 设置了Timeout参数的Object…wait0方法。
    • 设置了Timeout参数的Thread,.join()方法。
    • LockSupport.parkNanos()方法。
    • LockSupport…parkUntilO)方法。
  5. 阻塞(Blocked):进程被阻塞了,“阻塞状态”与“等待状态”的区别是:“阻塞状态”在等待着获取到一个排它锁,这个事件将在另外一个线程放弃这个锁的时候发生:而“等待状态”则是在等待一段时间,或者唤醒动作的发生。在程序等待进入同步区域的时候,线程将进入这种状态。

  6. 结束(Terminated):已终止线程的线程状态,线程已经结束执行。

线程基础知识_第1张图片

3. 线程的中断方式

  1. 布尔值标记的运用,以往有的时候会使用stop()方法来停止线程,但现在的JDK早就废除了stop()方法,不建议使用,现在提倡在run()方法中使用无限循环的形式,然后使用一个布尔型标记控制循环的停止
class MyRunnable : Runnable {
    var flag = true

    override fun run() {
        var i = 0
        while (flag) {
            Thread.sleep(500)
            println("i:${i++}")
        }
         println("终止了")
    }

    fun close() {
        flag = false
    }
}


fun main() {
    val myRunnable = MyRunnable()
    Thread(myRunnable).start()
    Thread.sleep(5000)
    myRunnable.close()
}
/*
i:0
i:1
i:2
i:3
i:4
i:5
i:6
i:7
i:8
i:9
终止了
*/
  1. 另外一个就是interrupt()方法,如果线程使用sleep()或wait()方法进入了就绪状态,那么可以使用interrupt()方法是线程离开run()方法,同时结束线程,但会抛InterruptedException异常,用户可以在处理异常时完成线程的中断业务处理,如终止while循环
class MyRunnable : Runnable {

    var flag = true
    override fun run() {
        var i = 0
        while (flag) {
            try {
                Thread.sleep(500)
            } catch (e: Exception) {
                flag = false
            }
            println("i:${i++}")
        }
        println("终止了")
    }
}

fun main() {
    val myRunnable = MyRunnable()
    val thread = Thread(myRunnable)
    thread.start()
    Thread.sleep(5000)
    thread.interrupt()
}
/*
i:0
i:1
i:2
i:3
i:4
i:5
i:6
i:7
i:8
i:9
终止了
*/

4. wait/notify

在 Java 中,使用 Object.wait()/Object.wait(long) 和 Object.notify()/Object.notifyAll() 可以用于实现等待和通知。用法省略。

原理:
JVM 会给每个对象维护一个入口集(Entry Set)和等待集(Wait Set)。

入口集用于存储申请该对象内部锁的线程,等待集用于存储对象上的等待线程。

wait() 方法会将当前线程暂停,在释放内部锁时,会将当前线程存入该方法所属的对象等待集中。

调用对象的 notify() 方法,会让该对象的等待集中的任意一个线程唤醒,被唤醒的线程会继续留在对象的等待集中,直到该线程再次持有对应的内部锁时,wait() 方法就会把当前线程从对象的等待集中移除。

添加当前线程到等待集、暂停当前线程、释放锁以及把唤醒后的线程从对象的等待集中移除,都是在 wait() 方法中实现的。

在 wait() 方法的 native 代码中,会判断线程是否持有当前对象的内部锁,如果没有的话,就会报非法监视器状态异常,这也就是为什么要在同步代码块中执行 wait() 方法。

5. await/signal

wait()/notify() 过于底层,而且还存在两个问题,一是过早唤醒、二是无法区分 Object.wait(ms) 返回是由于等待超时还是被通知线程唤醒。

使用 await/signal 协作方式有下面几个要点。

  • Condition 接口
    在 JDK 5 中引入了 Condition(条件变量) 接口,使用 Condition 也可以实现等待/通知,而且不存在上面提到的两个问题。
    Condition 接口提供的 await()/signal()/signalAll() 相当于是 Object 提供的 wait()/notify()/notifyAll()。
    通过 Lock.newCondition() 可以获得一个 Condition 实例。
  • 持有锁
    与 wait/notify 类似,wait/notify 需要线程持有所属对象的内部锁,而 await/signal 要求线程持有 Condition 实例的显式锁。
  • 等待队列
    Condition 实例也叫条件变量或条件队列,每个 Condition 实例内部都维护了一个用于存储等待线程的等待队列,相当于是 Object 中的等待集。
  • 循环语句
    对于保护条件的判断和 await() 方法的调用,要放在循环语句中
  • 引导区内
    循环语句和执行目标动作要放在同一个显式锁引导的临界区中,这么做是为了避免欺骗性唤醒和信号丢失的问题

基本用法:

class AwaitSignal  {
    private val lock: Lock = ReentrantLock()
    private val condition: Condition = lock.newCondition()

    @Volatile
    private var conditionSatisfied = false

    fun startWait() {
        lock.lock()
        println("等待线程获取了锁")
        try {
            while (!conditionSatisfied) {
                println("保护条件不成立,等待线程进入等待状态")
                condition.await()
            }
            println("等待线程被唤醒,开始执行目标动作")
        } catch (e: InterruptedException) {
            e.printStackTrace()
        } finally {
            lock.unlock()
            println("等待线程释放了锁")
        }
    }

    fun startNotify() {
        lock.lock()
        println("通知线程获取了锁")
        try {
            conditionSatisfied = true
            println("通知线程即将唤醒等待线程")
            condition.signal()
        } finally {
            println("通知线程释放了锁")
            lock.unlock()
        }
    }
}

fun main() {
    val awaitSignal = AwaitSignal()
    thread {
        awaitSignal.startWait()
    }

    thread {
        awaitSignal.startNotify()
    }
}
/*
等待线程获取了锁
保护条件不成立,等待线程进入等待状态
通知线程获取了锁
通知线程即将唤醒等待线程
通知线程释放了锁
等待线程被唤醒,开始执行目标动作
等待线程释放了锁
*/

6. LockSupport

LockSupport是一个线程阻塞工具类,所有的方法都是静态方法,可以让线程在任意位置阻塞,当然阻塞之后肯定得有唤醒的方法,主要有两类方法:parkunpark,另外线程中断的方法也能解除park阻塞状态

class LockSupportS  :Runnable{
    var u = Any()

    override fun run() {
        synchronized(u) {
            println("in ${Thread.currentThread().name}")
            LockSupport.park()
            if (Thread.currentThread().isInterrupted) {
                println("被中断了")
            }
            println("继续执行")
        }
    }
}

fun main() {
    val lockSupport = LockSupportS()
    val t1 = Thread(lockSupport, "t1")
    t1.start()
    Thread.sleep(1000)
    val t2 = Thread(lockSupport, "t2")
    t2.start()
    Thread.sleep(1000)
    // t1.interrupt()
    LockSupport.unpark(t1)
    LockSupport.unpark(t2)
}
/*
in t1
继续执行
in t2
继续执行

*/

你可能感兴趣的:(java,kotlin)