进程和线程
- 进程:,计算机中的程序关于某数据集合的一次运行过冬,是系统进行资源分配和调度的基本单位,是操作系统结构的基础,是线程的容器
- 线程:轻量级进程,程序执行的最小单位
- 线程生命周期:
- 线程状态:
NEW:刚创建的线程,还没开始执行(new Thread())
Runnbale:线程执行start()方法时候,进入RUNNABLE状态,表示线程需要的资源已经准备好
BLOCKED:当执行过程中遇到synchronized同步快,就会进入BLOCKED阻塞状态,线程暂停执行,指到获得请求的锁
WAITING:无时间限制的等待
TIME_WAITING:有时间限制的等待
等待的线程正是在等待一些特殊的事件,比如wait()方法等待的线程在等待notify()方法。join()方法等待的线程则会等待目标线程的终止。当等待结束后,线程会再次执行,进入RUNNABLE状态
TERMINATED:线程执行完毕
线程的基本操作
1. 新建线程
- 继承Thread类
使用创建线程直接使用new关键创建一个线程对象:
class MyThread extend Thread{}
Thread t1 = new MyThread();
t1.start();
start()方法会新建一个线程并让这个线程执行run()方法
注意:如果线程对象直接调用run()方法时,可以通过编译,也能正常执行,但是不能新建线程,而是作为一个普通的方法调用
一般情况下是要重载run()方法,因为Thread的run()方法没有业务逻辑,具体需求需要重载run()方法,然后添加业务逻辑代码 - 实现Runnable接口
Runnable接口只有一个run方法
此外,Thread类有一个构造方法,参数就是Runnable实例
public Thread(Runnable target)
继承Runnable接口创建线程:
public MyRunnable implements Runnable{}
Thread t1 = new Thread(new MyRunnable);
t1.start();
2. 终止线程
一般情况下,线程执行完毕后就会结束,无须手工关闭。但是对于一些常驻系统,通常不会正常终结
Thread提供了stop()方法,立即将一个线程结束,但是这是一个被废弃的方法,因为stop()方法会强行把执行到一半的线程终止,会导致数据错乱等问题
stop()会直接终止线程,并且会立即释放这个线程所持有的锁。在高并发多线程的程序中不要轻易使用stop()方法来停止一个线程
停止线程:可以根据具体需求,提供一个需要停止线程的flag,然后判断这个flag的值,在适当的时候break,就可以停止线程
3. 线程中断
线程中断并不会使形成立即退出,而是给线程发送一个通知,告知目标线程,需要退出,线程接到通知,何时退出就是目标线程自己决定
线程中断三个方法:
Thread.interrupt():通知目标线程中断,设置中断标志位
Thread.isInterrupted():判断当前线程时候有被中断
第三个Thread.interrupted():判断当前线程的中断状态,同时也会清除当前线程的中断标志位状态
sleep()方法:让线程休眠若干时间,会抛出InterruptedException中断异常,当sleep()休眠时,如果被中断就会抛出异常
sleep()由于中断而抛出异常,会清除中断标记。可以通过静态Thread.interrupted()中断方法来判断是否处于中断状态
4. 等待和通知
Object类中提供了wait()和notify()方法
wait():进入线程等待状态,线程停止继续执行。进入到object对象的等待队列(可能会有多个线程)
notify();通知线程可以结束等待状态,线程继续执行。被调用时,会从object对象的等待队列中,随机选择一个线程,并将其换新。这个选择是不公平的
notifyAll():唤醒等待队列中所有的等待的线程
注意:wait()方法必须包含在对应synchronzied语句中,无论是wait()还是notify()都需要首先获得目标对象的一个监视器(锁)
当wait()和notif()方法执行完后,会释放监视器(锁)。这样才不会导致其他等待的线程陷入长期休眠而无法正常执行
slee和wait的区别:
都是让线程等待若干时间
不同点:
wait方法可以被唤醒,还会释放对象的锁,属于object类
sleep类不会释放任何资源,属于Thread类
5. 挂起(suspend)和继续执行(resume)线程
线程挂起必须等到resume()操作后,才能继续指定。这两个方法已被标注为废弃方法
suspend()方法会导致线程暂停的同时并不会释放锁资源,而当其他线程刚要访问被锁住的资源的时候,就会导致线程阻塞,无法正常继续运行。直到线程进行了resume()操作,被挂起的线程才能继续。如此被阻塞的线程才能继续执行。
特殊情况:resume()在suspend()之前被执行,那么被挂起的线程很难有机会被继续执行,而且说占用的说不会被释放,就会造成系统无法正常工作
着重之点:被挂起的线程的状态还是Runnable,这会对于判断线程状态造成很大干扰
实现线程挂起和继续执行可以通过设置一个flag,通过配合wait()和notify()方法实现。当线程挂起的时候,执行wait()进行等待,当继续执行的时候,执行notfiy()通知,并且继续执行线程
6. 等待线程结束(join)和谦让(yield)
一个线程的输入可能非常依赖于另外一个或多个线程的输出,这时候,这个线程就需要等待依赖线程执行完毕,才能继续执行,可以通过join()来实现
public final void join() throws InterruptedExcetion:
无限等待,会一直阻塞当前线程,直到目标线程执行完毕
public final synchronized void join(long millis) throw InterruptedException
给定一个最大等待时间,如果超过给定时间目标线程还在执行,当前线程会因为超时而继续往下执行
join的本质是让调用线程在当前线程对象上进行等待
注意:不要在应用程序中,在Thread对象实例上使用wait()或者notif()等方法,因为可能会影响系统API的工作
public static native void yield();
执行这个方法之后,会使当前线程让出CPU。当前线程让出CPU后,还会进行CPU资源的争夺,是否会再次被分配到,就不一定过了