总结自《Java编程思想》第21章并发(P650~)
引用自《计算机操作系统》第四版,P76
类别 | 进程 | 线程 |
---|---|---|
调度基本单位 | 传统OS中进程是调度的基本单位 | 引入线程的OS中,线程是调度的基本单位 |
并发性 | 进程间可并发执行 | 进程中的多个线程可并发执行 |
拥有资源 | 系统拥有资源的一个基本单位 | 不拥有系统资源,仅拥有保证独立运行的少量资源 |
独立性 | 进程拥有自己独立的地址空间和其它资源,其它进程不可访问 | 同一进程的不同线程共享进程的内存地址空间和资源 |
系统开销 | 创建与撤消进程时,OS需要大量的开销 | OS创建开销小,并且上下文切换远比进程快 |
支持多处理系统 | 传统单线程进程只能运行在一个处理机上 | 多线程进程可将进程的多个线程分配到多个处理机上并发执行 |
通信 | 管道、FIFO、消息队列、信号量、共享存储、Socket | 线程共享同一进程中的内存与资源,可直接通信 |
状态 | 创建、就绪、阻塞、执行、终止 | 新建、就绪、阻塞、死亡 |
进程与线程都是顺序执行程序,一个线程就是在进程中的一个单一的顺序控制流。进程与线程都是可以解决并发编程的问题,线程是轻量级的进程,线程诞生于进程中。线程的底层机制是切分CPU时间,CPU将轮流给每个任务分配其占用时间
Java中实现了接口java.lang.Runable的类就可以称之为任务,其实现方法run()中编写的即是需要并发执行的代码。
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface Runnable
is used
* to create a thread, starting the thread causes the object's
* run
method to be called in that separately executing
* thread.
*
* The general contract of the method run
is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
线程在Java中只有一个类表示,那就是java.lang.Thread,简单启动一个线程的代码如下
fun main(args: Array) {
println(Thread.currentThread().name)
val t = Thread(Runnable {
print("ThreadName = ${Thread.currentThread().name} it's other thread")
})
t.start()
}
/*output
main
ThreadName = Thread-0 it's other thread
*/
创建Thread时在其构造函数中传入一个Runable类参数,当线程调用start()方法时,即会驱动Runable中代码段的执行
线程不是任务,这两者的概念一定要区分清楚。Thread类自身不执行任何操作,它只是驱动赋予它的任务。
下面我将用一张图来通俗地讲解一个线程驱动任务运行的过程
继承类通用重写thread中的run方法,然后生成实例调用start(),即可运行一个线程
在传统方式中将start()的调用放至自定义类中的构造方法中执行,从而在生成实例时即开始运行一个线程。
实例如下
class SimpleThread : Thread{
private var countDown = 5
companion object {
private var threadcount = 0
}
constructor():this(Integer.toString(++threadcount))
constructor(name: String): super(name)
init {
start() //聚合方式
}
override fun toString(): String {
return "#$name($countDown)"
}
override fun run() {
while (true){
print(this)
if(--countDown == 0) return
}
}
}
fun main(args: Array) {
for(i in 1..5){
SimpleThread()
//SimpleThread().start() 传统方式启动线程
}
}
/*output 输出结果是不确定的,有各种可能
#2(5)#2(4)#1(5)#2(3)#3(5)#2(2)#1(4)#1(3)#4(5)#2(1)#5(5)#3(4)#5(4)#4(4)#1(2)#4(3)#5(3)#3(3)#5(2)#4(2)#1(1)#4(1)#5(1)#3(2)#3(1)
or
1(5)#2(5)#3(5)#1(4)#2(4)#1(3)#3(4)#1(2)#4(5)#2(3)#4(4)#1(1)#3(3)#4(3)#2(2)#4(2)#3(2)#4(1)#2(1)#3(1)#5(5)#5(4)#5(3)#5(2)#5(1)
*/
自定义类实现Runable接口,重写run方法,然后创建一个Thread类对象,在构造器中传入一个Runable的对象,最后调用Thread对象的start()运行线程,如2.2代码所示
在自定义类中持有一个Thread对象,然后在构造器中调用Thread#start()运行线程,即实现一个自管理的Runable.
实例如下:
class SelfManaged: Runnable{
private var countDown = 5
private var t = Thread(this)
init {
t.start()
}
override fun toString(): String {
return "${Thread.currentThread().name}($countDown)"
}
override fun run() {
while (true){
print(this)
if(--countDown == 0){
return
}
}
}
}
fun main(args: Array) {
for(i in 1..5){
SelfManaged()
}
}
/*output
Thread-1(5)Thread-2(5)Thread-0(5)Thread-2(4)Thread-1(4)Thread-2(3)Thread-0(4)Thread-2(2)Thread-1(3) Thread-2(1)Thread-0(3)Thread-1(2)Thread-0(2)Thread-1(1)Thread-0(1)Thread-3(5)Thread-3(4)Thread-3(3) Thread-3(2)Thread-3(1)Thread-4(5)Thread-4(4)Thread-4(3)Thread-4(2)Thread-4(1)
*/
class InnerThread(name: String){
private var countDown = 5
private var inner1:Inner
init {
inner1 = Inner(name)
}
private inner class Inner : Thread{
constructor(name:String): super(name){
start()
}
override fun run() {
try {
while (true){
println(this)
if(--countDown == 0 ) return
sleep(10)
}
}catch (e:InterruptedException){
}
}
override fun toString(): String {
return "$name:$countDown"
}
}
}
fun main(args: Array) {
InnerThread("InnerThread")
}
/*output
InnerThread:5
InnerThread:4
InnerThread:3
InnerThread:2
InnerThread:1
*/
Thread实现了Runable接口,所以可通过继承Thread重写run方法的方式来实现多线程。
public class Thread implements Runnable {}
private volatile char name[];
Thread.currentThread()
来获取当前运行的线程对象 public String toString() {
ThreadGroup group = getThreadGroup();
if (group != null) {
return "Thread[" + getName() + "," + getPriority() + "," +
group.getName() + "]";
} else {
return "Thread[" + getName() + "," + getPriority() + "," +
"" + "]";
}
}
private int priority;
setPriority()、getPriority()
进行线程优先级的设置与获取。MAX_PRIORITY,NORM_PRIORITY,MIN_PRIORITY
这三种级别,默认是NORM_PRIORITY = 5
。实例如下:
class SimpleDaemons: Runnable{
override fun run() {
try {
while (true){
TimeUnit.MILLISECONDS.sleep(100)
println("${Thread.currentThread()} $this")
}
}catch (e: InterruptedException){
println("sleep() interrupted")
}
}
}
fun main(args: Array) {
for(i in 0 until 10){
val daemon = Thread(SimpleDaemons())
daemon.isDaemon = true //设置线程为后台线程,必须在Start之前调用
daemon.start()
}
println("All daemons started")
TimeUnit.MILLISECONDS.sleep(99) //当睡眠时间为99ms时小于子线程的睡眠时间100ms时,所有后台子线程都不会执行
}
/**output
99ms
All daemons started
100ms 有可能执行全部或部分子线程,也可能都不执行
All daemons started
Thread[Thread-8,5,main] mutilthread.SimpleDaemons@1857af2f
Thread[Thread-9,5,main] mutilthread.SimpleDaemons@2afd6006
Thread[Thread-7,5,main] mutilthread.SimpleDaemons@4c630b0d
Thread[Thread-6,5,main] mutilthread.SimpleDaemons@61e0162
Thread[Thread-5,5,main] mutilthread.SimpleDaemons@21f189d9
Thread[Thread-4,5,main] mutilthread.SimpleDaemons@488a7f84
*/
结论是当main线程停止执行时程序退出,所有的后台线程都跟着一起终止不会得到执行