线程简介
学习多线程之前,我们先要了解几个关于多线程有关的概念。
进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程
什么是多线程呢?即就是一个程序中有多个线程在同时执行。
单线程程序与多线程程序的不同:
-
单线程程序:即,若有多个任务只能依次执行。当上一个任务执行结束后,下一个任务开始执行。如,去网吧上网,网吧只能让一个人上网,当这个人下机后,下一个人才能上网。
-
多线程程序:即,若有多个任务可以同时执行。如,去网吧上网,网吧能够让多个人同时上网。
线程的创建
1、继承 Thread
类,并覆盖 run()
方法
Thread 的实现类继承 Thread
类,并覆盖其 run()
方法,run()
方法中定义线程需要执行的任务,并调用实现类的 start()
方法创建线程.
class MyThread extends Thread {
@Override
public void run() {
System.out.println("子线程启动,ID为:" + Thread.currentThread().getId() + ",名字为:" + Thread.currentThread().getName());
}
}
class Test{
public static void main(String[] args) {
// 创建一个线程并开启线程
MyThread thread=new MyThread();
thread.start();
// 多创建几个线程
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
输出如下:
子线程启动,ID为:13名字为Thread-0
子线程启动,ID为:15名字为Thread-2
子线程启动,ID为:16名字为Thread-3
子线程启动,ID为:14名字为Thread-1
2、实现 Runnable
接口,并覆盖 run()
方法
因为 Java 是单继承的,因此直接继承 Thread 类常常并不是一个好主意.
我们常常通过实现 Runnable 接口或 Callable 接口定义一个任务,并将其传给 Thread 类构造函数来创建一个线程.其中 Runnable 类的 run()方法没有返回值,而 Callable 类的 call()方法可以有返回值.
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("子线程启动,ID为:" + Thread.currentThread().getId() + ",名字为" + Thread.currentThread().getName());
}
}
class Test{
public static void main(String[] args) {
// 通过将Runnable对象传入Thread构造函数来创建线程,并开启线程
Runnable runnable=new MyRunnable();
Thread thread1=new Thread(runnable,"线程1");
thread1.start();
// 一个Runnable对象可以用来创建多个线程
new Thread(runnable, "线程2").start();
new Thread(runnable, "线程3").start();
new Thread(runnable, "线程4").start();
}
}
输出如下:
子线程启动,ID为:14,名字为线程2
子线程启动,ID为:13,名字为线程1
子线程启动,ID为:16,名字为线程4
子线程启动,ID为:15,名字为线程3
3、实现 Callable
接口,并覆盖 call()
方法
通过该方法创建的任务可以有返回值.但 Callable
对象只能传给线程池,创建线程的具体方法见后边文章.
要注意
Callable
和Runnable
定义的都是任务而不是线程,要将其传入一个线程或线程池后才可以执行.
线程的状态
线程有五个状态:
- 新生(new)状态: 用 new 关键字建立一个线程对象后,该线程对象就处于新生状态,有自己的内存空间.
- 就绪(runnable)状态: 调用了 start()方法后,线程转为就绪状态,处于就绪状态线程具备了运行条件,但还没分配到 CPU,等待系统 CPU 调度.
- 运行(running)状态: 处于运行状态的线程正在执行自己的 run()方法中代码.
- 阻塞(blocked)状态: 线程暂停执行,让出 CPU 并将其交给其他线程使用.
- 死亡(dead)状态: 当线程完成工作或抛出异常时,线程死亡,不再执行.
线程控制基本方法
判断线程状态的方法
-
public long getId(): 得到线程的 ID
-
public String getName()和 public void setName(String name): 得到或设置线程名称
-
public boolean isAlive(): 判断当前线程是否处于活动状态
-
public int getPriority()
和public void setPriority(int newPriority)
: 得到或设置线程优先级。Java 中线程的优先级为 1~10 之间的整数,一个线程的默认优先级为 5。优先级的高低只是意味着获得调度概率的高低,并不代表调度的绝对顺序. -
public static Thread currentThread()
返回当前代码段正在被哪个线程调用.
阻塞线程的方法
public void join()
,public void join(long millis)
,public void join(long millis, int nanos)
:合并线程,调用某线程的 join()方法,会将当前线程与该线程合并,即等待该线程执行结束之后再恢复当前线程的运行。
join()
方法可以看作把并发的线程变为在一个线程内的函数调用
class MyThread extends Thread {
@Override
public void run() {
for (int i = 1; i <=3 ; i++) {
System.out.println(Thread.currentThread().getName() + ":" + i);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Test{
public static void main(String[] args) throws InterruptedException{
Thread thread1=new MyThread();
thread1.start();
thread1.join();
Thread thread2=new MyThread();
thread2.start();
for (int i = 1; i <=3 ; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
sleep(1000);
}
}
}
程序输出如下:
Thread-0:1
Thread-0:2
Thread-0:3
main:1
Thread-1:1
Thread-1:2
main:2
main:3
Thread-1:3
public static sleep(long millis)
,public void sleep(long millis, int nanos)
: 使线程停止运行一段时间并转入阻塞状态.
sleep()
方法不会交出锁.- 因为
sleep()
方法使线程进入阻塞状态
,因此若调用了sleep()
方法后,即使没有其他等待执行的线程,当前线程也不会马上恢复执行.
public static void yield()
: 礼让线程,让当前正在执行线程暂停并转入就绪状态.
yield()
方法不会交出锁.- 因为
yield()
方法使线程进入就绪状态,因此若调用了yield()
方法后,没有其他等待执行的线程,当前线程就会马上恢复执行.
学习资源
多线程的学习主要是在 B 站上看的马士兵老师的 java 多线程高并发编程
https://www.bilibili.com/video/av33688545
博客总结参考评论区一位大佬的博客
https://blog.csdn.net/ncepu_Chen/article/details/94615325
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于