一、程序、进程与线程
程序:Program
程序是一段静态代码
进程:Process
进程是指一种正在运行的程序,有自己的地址空间
特点:
动态性,并发性,独立性
并发和并行的区别:
并发: 多个cpu同时执行多个任务
并行: 一个cpu同时执行多个任务
线程:Thread
线程是进程内部的一个执行单元,它是程序中一个单一的顺序控制流程。
线程又是轻量级的进程。
如果在一个进程中运行多个线程,用来完成不同工作,则称之为多线程。
线程的特点:
轻量级进程。
独立调度的基本单位。
可并发执行。
共享进程资源。
进程与线程的区别:
二、线程的创建和启动
1.线程的创建方式比较:
1.1 继承Thread类方式的多线程:
优势:编写简单
劣势:无法继承其他父类,只能单继承
1.2 实现Runnable接口方式的多线程(比较常用)
优势:可以继承其他类,多线程可共享同一个Runnable对象
劣势:编程方式稍微复杂,如果需要访问当前线程,需要调用Thread.currentThread()方法。
线程没有返回值
1.3 实现Callable接口
优势: 与实现Runnbale相比,Callable功能更加强大
方法不同
可以有返回值,支持泛型的返回值
可以抛出异常
需要借助FutureTask,比如获取返回结果
2.线程的创建
方式1:继承Java.lang.Thread类,并覆盖run()方法
方式2:实现Java.lang.Runnable接口,并实现run()方法
方式3:实现Callable接口,并实现call()方法
3.线程的启动
a.新建的线程不会自动开始执行,必须通过start()方法启动
b.不能直接调用run()来启动线程,这样run()将作为一个普通方法立即执行,执行完毕前其它线程无法执行
c.Java程序启动时,会立刻创建主线程,main就是在这个线程上运行的。当不再产生新线程时,程序是单线程的
三、线程的生命周期
新生状态:
用new关键字建立一个线程对象后,该线程对象就处于新生状态。
处于新生状态的线程拥有自己的内存空间,可以通过调用start进入就绪状态。
就绪状态:
处于就绪状态的线程具备了运行条件,但还没分配cpu,处于线程就绪队列,等待系统为其分配cpu。
当系统选定一个等待执行的线程后,它就会从就绪状态进入执行状态,改动做成为"cpu调度"。
运行状态:
在运行状态的线程执行自己run方法中的代码,直到等待某资源而堵塞或完成任务死亡。
如果给定的时间片内没有执行结束,就会被系统给换下来回到等待执行状态。
堵塞状态:
处于运行状态的线程在某些情况下,如执行了sleep()方法,或等待I/O设备等资源,将让出cpu并暂时停止自己的运行,进入堵塞状态。
在堵塞状态下的线程不能进入就绪队列。只有当引起堵塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便进入就绪状态,重新到就绪状态队列排队等待,被系统选中时从原来的位置继续运行。
死亡状态:
死亡状态是线程生命周期的最后一个阶段。线程死亡的原因有三个:1.正常运行的线程完成了它的全部工作。2.线程被强制的终止,如通过stop()方法停止执行(不推荐使用)。3.线程抛出未捕获的异常。
四、线程控制方法
优先级:
线程的优先级用数字表示,范围从1到10
Thread.MIN_PRIORITY = 1 最低优先级
Thread.MAX_PRIORITY = 10 最高优先级
Thread.NORM_PRIORITY = 5 默认优先级
获得或设置线程对象的优先级
int getPriority()
void setPriority(int newPriority);
注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高后调用优先级低的线程。
控制线程的方法
join()
堵塞指定线程等到另一个线程完成以后再继续执行
sleep()
使线程停止运行一段时间,将处于堵塞状态
如果调用了sleep方法后,没有其他等待执行的线程,这个时候当前线程不会马上恢复执行
yield()
让当前正在执行的停止,不是堵塞线程,而是将线程转入就绪状态
如果调用了yield方法后,没有其他等待执行的线程,这个时候当前线程会马上恢复执行
setDeamon()
可以将指定的线程设置成后台线程
创建后台程序结束时,后台线程也会随之消亡
只能在线程启动前将它设置为后台线程
interrupt()
并没有直接中断线程,而是需要被中断线程自己处理
stop()
结束线程,不推荐使用
五、线程同步
原因:
当多个线程访问同一个数据时,容易出现线程安全问题。需要让线程同步,保证数据安全。
线程同步:
当两个或两个以上线程访问同一资源时,需要某种方式来确保资源在某一时刻只被一个线程使用
线程同步的实现方案:
同步代码块:
synchronized (对象|属性|类字节码) {
需要同步的java代码
}
同步方法:
public synchronized void test(){
}
线程同步优缺点:
优点:解决了线程安全问题
缺点:性能下降,会带来死锁
死锁:
当两个线程互相等待对方释放锁时就会发生死锁。
出现死锁时不会出现异常,不会出现提示,只是所有的线程都处于堵塞,无法继续
多线程编程时应该注意避免死锁的发生
死锁的发生场景:生产者和消费者问题: