一、概念
这里先介绍线程的定义,线程与进程的区别,线程的生命周期这些重要的知识点。
1 . 线程的定义:
一个程序可以同时执行多个任务,每一个任务称为一个线程。2 .线程与进程的具体区别:
1.最小单位:进程是资源分配的最小单位,线程是cpu调度的最小单位。
2.地址空间:进程拥有独立的地址空间,线程共享地址空间。( 因为共享所以线程开销小,通信方便,同时也可能因一个线程的死亡造成共享资源的其他线程的错误)
3.选择:如果需要程序更加的稳定安全,使用进程。如果追求速度,使用多线程。
3 . 线程的生命周期:
- New状态:创建了一个线程对象时。
- Runnable状态:就绪状态,调用了线程对象的start()时进入。(这时等待操作系统的资源)
- Blocked状态:阻塞状态,是一个线程因为等待临界区的锁而被阻塞产生的状态。(因为ReentrantLock或者synchronized产生的状态)
- Waiting状态:获得lock的线程需要等待其他线程执行某些操作。(调用了无参数的Object.wait()或者是Thread.join或者LockSupport.park()产生的状态)
- Timed_Waiting状态:限时等待状态。在限定时间内如果未被唤醒,就自己醒来,如果有其他线程竞争锁,就从等待池到锁池中参与竞争。(调用了带时间参数的Object.wait(TimeUnit)或者是Thread.join或者LockSupport.park()产生的状态)
- Terminated状态:最终状态,代表线程的整个生命周期结束。(进入Terminated状态的情况:①运行正常结束;②出错意外结束;③JVM crash导致所有进程结束)
二、创建线程
共有四种方法创建线程:
1. 继承Thread:
要点:
(1)extends Thread
(2)重写run()
(3)调用成员方法start启动
class myThread extends Thread{
@Override
public void run() {
}
public static void main(String[] args) {
myThread thread = new myThread();
thread.start();
}
}
2.接口Runnable:
要点:
(1)implements Runnable
(2)重写run()
(3)该实例作为参数,实际用Thread创建
(4)同样start()启动
class myThread implements Runnable{
@Override
public void run() {
}
public static void main(String[] args) {
Thread thread = new Thread(new myThread());//**注意这里
thread.start();
}
}
3.匿名线程
要点:
(1)重写run()
(2)末尾.start()
(3)new Thread()括号中是线程名,通过.getName()可得(其他方法中同理)
new Thread("Bonus"){
@Override
public void run() {
System.out.println(this.getName());
}
}.start();
4.接口Callable
要点:
(1)implements Callable<>
(2)重写call()
(3)该类实例作为参数创建FutureTask类实例,再用FutureTask类实例作为参数创建Thread类实例
(4)通过Future对象可了解任务执行情况,可取消任务的执行,还可获取任务执行的结果。
(5)call()是可以抛出异常的,相比之下run()并不可以抛异常。
class a implements Callable<String> {
@Override
public String call() {
return "Blessing WuHan武汉加油";
}
public static void main(String[] args) {
FutureTask<String> futureTask = new FutureTask<>(new a());
new Thread(futureTask).start();
try {
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}