多线程简介
对多线程的理解
1. 多任务与多线程
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
2. 多线程例子(生活,游戏,编程)
-
边吃饭边玩手机,边听歌边写作业
-
开黑打游戏
-
等等
3. 进程,程序,线程的理解
-
进程: 是程序的一次执行,是动态的,一个进程至少包含一个线程,一个线程不能独立存在,它必须是进程的一部分;一个进程一直运行,直到所有非守护线程都结束运行后才能结束。
-
程序是代码段,是静态的。
-
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。线程是一个动态执行的过程,它也有一个从产生到死亡的过程。
3. 核心概念
-
线程是独立的执行路径;
-
在程序运行时,即使没有自己的创建的线程,后台也会有多个线程,如主线程,gc线程;
-
main()为主线程,是程序的入口,用于执行整个程序;
-
在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器由操作系统的调度算法决定,人为不可干涉;
-
对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
-
线程会带来额外的开销,如CPU调度的时间,并发控制的开销;
-
每个线程在自己的工作内存交互,内存控制不当会造成数据不一致;
线程实现
线程的优先级
每一个 Java 线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。
创建线程
- 通过继承Thread类线程创建线程
public class MyThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("MyThread is running!!! " + i);
}
}
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("Main Thread is running " + i);
}
}
}
- 通过实现Runnable接口创建线程
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable R1 = new MyRunnable("Thread-1");
R1.start();
MyRunnable R2 = new MyRunnable("Thread-2");
R2.start();
for (int i = 0; i < 3 ; i++) {
System.out.println("这是主线程main");
}
}
}
class MyRunnable implements Runnable {
private Thread t;
private String threadName;
public MyRunnable(String name) {
threadName = name;
System.out.println("Creating: " + threadName);
}
@Override
public void run() {
System.out.println("Running: " + threadName );
try {
for (int i = 4; i > 0; i--) {
System.out.println("Thread: " + threadName + "," + i);
Thread.sleep(50);
}
} catch (InterruptedException e) {
System.out.println("Thread: " + threadName + "interrupted.");
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start() {
System.out.println("Starting " + threadName);
if (t == null) {
t = new Thread(this, threadName);
t.start();
}
}
}
- 通过Callable接口创建线程
静态代理
- 真实对象和代理对象都要实现同一个接口
- 代理对象要代理真实对象
- 优点:
- 代理对象可以做很多真实对象做不了的事情
- 真实对象专注于自己的事情
线程状态
- 线程的生命周期
1. 新建状态
使用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序star()这个线程。
2. 就绪状态
当线程对象调用start()方法后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待JVM里的线程调度器的调度。
3. 执行状态
如果就绪队列中的线程获得了处理机,就可以执行run()方法此时线程便处于运行状态。
4. 阻塞状态
当一个线程执行了sleep(),suspend()等方法后,失去所占用资源之后,该线程就从运行态转化为阻塞态。在睡眠时间已到或获得设备资源后就可以重新进入就绪态。
阻塞态可以分为三种:
-
等待阻塞:运行态中的线程执行了wait()方法,使线程进入等待阻塞态
-
同步阻塞:线程在获取synchronized(同步)同步锁失败(因为同步锁被其他线程占用)。
-
其他阻塞:在调用线程的sleep()或join()发出的I/O请求时,线程会进入到阻塞态,当sleep()态超时,join()等待线程终止或超时时,或者I/O处理完毕,线程重新转入就绪态。
5. 终止状态
一个运行态的线程完成任务或其他终止条件发生时,该线程就切换到终止态;
线程控制常用方法
方法 | 说明 |
---|---|
setPriority(int newPriority) | 更改线程的优先级 |
static void sleep(long millisecond) | 在指定毫秒数内让当前正在执行的线程休眠 |
void join() | 等待该线程终止 |
static void yield | 暂停正在执行的线程对象,并执行其他线程 |
void interrupt() | 中段线程 |
boolean isAlive() | 检测线程是否处于活动状态 |
线程的生命周期图示
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sxk0v3Ix-1623500327402)(https://i.loli.net/2020/11/16/R2PTzGVN5OAKUx8.png)]
线程控制
停止线程
代码演示:
// 1.建议线程正常停止 --> 利用次数, 不建议使用死循环
// 2.建议使用标准位来控制线程,让其自己停下来
// 3.不建议使用stop()和destroy()
public class Stop implements Runnable {
// 1.设置一个标志位
private boolean flag = true;
@Override
public void run() {
int i = 0;
while (this.flag) {
System.out.println("Thread Running " + i++);
}
}
// 2.设置一个公开的方法停止线程, 转换标志位
public void stop() {
this.flag = false;
}
public static void main(String[]