一. 概念
是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
二. 使用多线程的目的
1.为了更好的利用cpu的资源,如果只有一个线程,则第二个任务必须等到第一个任务结束后才能进行,如果使用多线程则在主线程执行任务的同时可以执行其他任务,而不需要等待。
2.进程之间不能共享数据,线程可以。
3.系统创建进程需要为该进程重新分配系统资源,创建线程代价比较小。
4.Java语言内置了多线程功能支持,简化了java多线程编程。
三.线程的生命周期
线程的生命周期包含5个阶段,包括:新建、就绪、运行、阻塞、销毁。
1.新建:使用new方法,new出来的线程。
2.就绪:调用的线程的start()方法后,这时候线程处于等待CPU分配资源阶段,谁先抢的CPU资源,谁开始执行。
3.运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态,run方法定义了线程的操作和功能。
4.阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候需要其他机制将处于阻塞状态的线程唤醒,比如调用notify或者notifyAll()方法。唤醒的线程不会立刻执行run方法,它们要再次等待CPU分配资源进入运行状态。
5.销毁:如果线程正常执行完毕后或线程被提前强制性的终止或出现异常导致结束,那么线程就要被销毁,释放资源。
四.并发与并行,同步与异步,高并发,临界区
概念 | 描述 |
---|---|
并发 | 多个任务交替执行,但是多个任务之间有可能还是串行的 |
并行 | 真正意义上的“同时执行” |
同步 | 方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为 |
异步 | 方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者可以继续后续的操作 |
高并发 | 互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求 |
临界区 | 用来表示一种公共资源或者说是共享数据,可以被多个线程使用。但是每一次,只能有一个线程使用它,一旦临界区资源被占用,其他线程要想使用这个资源,就必须等待。在并行程序中,临界区资源是保护的对象 |
五.使用多线程常见的四种方式
1.继承Thread类创建线程
Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();
2.实现Runnable接口创建线程
如果自己的类已经extends另一个类,就无法直接extends Thread,此时,可以实现一个Runnable接口
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:
public void run() {
if (target != null) {
target.run();
}
}
3.实现Callable接口通过FutureTask包装器来创建Thread线程
4.使用ExecutorService、Callable、Future实现有返回结果的线程,在该方法中创建了线程池,使用线程池的方式也是最推荐的一种方式。
六.线程中的一些常用方法
- currentThread()
返回对当前正在执行的线程对象的引用。 - getId()
返回此线程的标识符 - getName()
返回此线程的名称 - getPriority()
返回此线程的优先级 - isAlive()
测试这个线程是否还处于活动状态。
活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备运行的状态。 - sleep(long millis)
使当前正在执行的线程以指定的毫秒数“休眠”(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 - interrupt()
中断这个线程。 - interrupted() 和isInterrupted()
interrupted():测试当前线程是否已经是中断状态,执行后具有将状态标志清除为false的功能。
isInterrupted(): 测试线程Thread对相关是否已经是中断状态,但部清楚状态标志。 - setName(String name)
将此线程的名称更改为等于参数 name 。 - isDaemon()
测试这个线程是否是守护线程。 - setDaemon(boolean on)
将此线程标记为 daemon线程或用户线程。 - join()
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是 主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行 - yield()
yield()方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU时间。注意:放弃的时间不确定,可能一会就会重新获得CPU时间片。 - setPriority(int newPriority)
更改此线程的优先级
七.线程的优先级
- 在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU资源较多,也就是CPU优先执行优先级较高的线程对象中的任务。设置线程优先级有助于帮线程规划器确定在下一次选择哪一个线程来有限执行。
- 在Java中,线程的优先级分为1到10这10个等级,如果小于1或者大于10,则JDK抛出异常IllegalArgumentException。
- 在JDK中使用3个变量来预置定义优先级的值:
public final static int MIN_PRIORITY =1;
public final static int NORM_PRIORITY =5;
public final static int MAX_PRIORITY =10;
八.多线程分类
-
用户线程:运行在前台,执行具体的任务,如程序的主线程、连接网络的子线程等都是用户线程
-
守护线程:运行在后台,为其他前台线程服务.也可以说守护线程是JVM中非守护线程的 “佣人”。
特点:一旦所有用户线程都结束运行,守护线程会随JVM一起结束工作
应用:数据库连接池中的检测线程,JVM虚拟机启动后的检测线程
最常见的守护线程:垃圾回收线程