System.out.println(“睡醒了”);
- 线程加入
public final void join()
:(让当前线程)等待该线程(新加入的线程终止)终止。
SecondThread sec = new SecondThread(“zs”);
SecondThread thr = new SecondThread(“lsii”);
SecondThread fou = new SecondThread(“wangwu”);
//启动这三个线程对象
sec.start();
//sec线程上调用join方法 ,让当前线程(main)等待sec线程执行完毕
sec.join();
thr.start();
fou.start();
- 线程礼让
public static void yield()
:暂停当前正在执行的线程对象,并执行其他线程
只是让当前线程放弃cpu执行权,但是不能阻止它放弃后继续抢夺cpu执行权
- 后台线程(守护线程)
public final void setDaemon(boolean on)
:将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出;该方法必须在启动线程前调用。
SecondThread sec = new SecondThread(“zs”);
SecondThread thr = new SecondThread(“lsii”);
//将sec和thr线程设置为守护线程
sec.setDaemon(true);
thr.setDaemon(true);
//启动
sec.start();
thr.start();
//之后发现sec和thr是守护线程,就会中断
- 中断线程:
public void interrupt()
:让一个线程控制另外一个线程(有条件的:受阻),可以利用该方法终止另一个线程的运行
1. 如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 等阻塞方法处于阻塞状态,它还将收到一个 InterruptedException
2. 中断一个不处于活动状态的线程不需要任何作用。
中断一个不处于阻塞状态的线程,没有其他任何效果
public class Test {
public static void main(String[] args) {
FiveThread fiveThread = new FiveThread();
fiveThread.start();
//在主线程中,终止fiveThread,休眠状态
fiveThread.interrupt();
}
}
// 睡5秒之后,再让该线程输出一句话
class FiveThread extends Thread{
@Override
public void run() {
try {
//假如申请了很多的系统资源
TimeUnit.SECONDS.sleep(5);
System.out.println(“FiveThread 睡醒了”);
} catch (InterruptedException e) {
e.printStackTrace();
//异常的意义:即使我的线程被异常终止,我也可以保证资源的正常释放
}
}
}
//会抛出 java.lang.InterruptedException: sleep interrupted
4. JAVA程序的运行原理
- Java命令会启动 JVM,即启动了一个进程,该进程会启动一个主线程,然后主线程调用某个类的 main方法,所以 main方法 都是运行在主线程里
- jvm 启动后,必然有一个执行路径(线程)从 main方法开始的,一直执行到 main方法结束,这个线程在Java中称之为
主线程
。 - 当程序的主线程执行时,如果遇到了循环而导致程序在指定位置停留时间过长,则无法马上执行下面的程序,需要等待循环结束后能够执行
方法在哪个线程中被调用,它就运行在哪个线程中
- JVM 是一个多线程程序,每个Java 进程都分配一个 JVM 实例
public class ThreadDemo {
public static void main(String[] args) {
//利用垃圾回收器来证明
while(true) {
//这里虽然一直在堆空间中,创建数组对象,
// 但是始终没有耗尽堆空间,就是因为垃圾回收器,
// 在另外一个线程中,帮我们回收垃圾,所以才不会耗尽heap内存
// 从而证明,jvm是线程的
int[] ints = new int[1024];
ints = null;
}
}
}
二、Thread 类
1. 概述
- Thread是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程
- 不是抽象类
2. 构造方法
Thread()
: 分配新的 Thread 对象Thread(String name)
:分配新的 Thread 对象,将指定的 name 作为其线程名称
3. 常用方法
void start()
:使该线程开始执行,Java虚拟机调用线程的 run 方法void run()
:该线程要执行的操作,static void sleep(long millis)
:在指定毫秒内让当前正在执行的线程休眠,暂停执行static Thread currentThread()
:返回当前正在执行的线程对象的引用Thread.currentThread()
4. 创建新执行线程的两种方法
- 将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,开启线程。run方法相当于其他线程的main方法。
- 声明一个实现 Runnable 接口的类。该类然后实现 run 方法。然后创建 Runnable 的子类对象,传入到某个线程的构造方法中,开启线程。
虽然实现线程有两种方式,其实从客观来讲,线程本身只代表独立的执行路径, 执行的具体内容其实是Task本身,和执行路径的实现本身没有联系;只是我们开发者,想将一个task放在某条独立的执行路径(Thread 类对象,也就是一个线程中)来运行
三、创建线程:继承 Thread 类
-
创建线程的步骤
-
定义一个类继承 Thread
-
重写 run方法
-
创建子类对象,就是创建线程对象
-
调用 start 方法,开启线程并让线程执行,同时还会告诉jvm去调用 run 方法
线程对象调用 run方法 不开启线程。仅是对象调用方法。
线程对象调用 start 开启线程,并让 jvm 调用 run 方法在开启的线程中执行
//测试类
public class Test {
public static void main(String[] args) {
//创建自定义线程对象
MyThread mt = new MyThread(“新的线程!”);
//错误启动线程,这只是普通的方法调用
//firstThread.run();
//开启新线程
mt.start();
//再次启动一个线程,会抛异常IllegalThreadStateException
//因为一个线程对象只能启动一次,
// 如果同一个线程对象,启动多次,就会抛出异常
//firstThread.start();
//只能创建一个新的对象
new MyThread(“第二个线程!”).start();
//获取主线程的名字
System.out.println(Thread.currentThread().getName()+“:主线程!”);
}
}
//自定义线程
class MyThread extends Thread {
//定义指定线程名称的构造方法
public MyThread(String name) {
//调用父类的String参数的构造方法,指定线程的名称
super(name);
}
//重写run方法,完成该线程执行的逻辑
@Override
public void run() {
//获取线程的名字getName()
System.out.println(getName() + “:正在执行!”);
}
}
四、创建线程:实现 Runnable 接口
1. Runnable 接口的构造方法
Thread(Runnable target)
: 分配新的 Thread 对象,以便将 target 作为其运行对象Thread(Runnable target,String name)
: 分配新的 Thread 对象,以便将 target 作为其运行对象;并将指定的 name 作为其名称
2. 创建线程的步骤
- 定义类实现 Runnable 接口。
- 覆盖接口中的 run方法
- 创建 Thread类的 对象
- 将 Runnable接口 的子类对象作为参数传递给 Thread 类 的构造方法。
- 调用 Thread类的 start() 开启线程。
Thread 类的构造函数:
1.Thread()
: 分配新的 Thread 对象
2.Thread(String name)
:分配新的 Thread 对象,将指定的 name 作为其线程名称
//测试类
public class Test {
public static void main(String[] args) {
//创建实现 Runnable 接口的子类对象
MyRunnable myrunnable = new MyRunnable();
//创建Thread实例,在Thread的构造方法中传递Runnable实例
//Runnable就代表在 Thread 上运行的任务
Thread thread = new Thread(myrunnable);
//开启线程
thread.start();
for (int i = 0; i < 10; i++) {
System.out.println(“main线程:正在执行!”+i);
}
}
}
//自定义线程执行任务类
class MyRunnable implements Runnable{
//定义线程要执行的run方法逻辑
//run方法,不能抛出编译时异常
//run方法,没有参数没有返回值
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(“我的线程:正在执行!”+i);
}
}
}
3. 实现 Runnable 接口的原理
为什么需要定一个类去实现Runnable接口呢?继承Thread类和实现Runnable接口有啥区别呢?
只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务
4. 两种方式的比较
-
继承 Thread 类方式
-
如果某个类已经有父类,则无法再继承 Thread 类
-
实现 Runnable 接口方式
-
解决了方式一的单继承的局限性
-
还有一个优点,便于多线程共享数据
第二种方式实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象;线程分为两部分,一部分线程对象,一部分线程任务。
- 继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。
- 实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦
五、线程优先级
1. 概述
一线互联网大厂Java核心面试题库
正逢面试跳槽季,给大家整理了大厂问到的一些面试真题,由于文章长度限制,只给大家展示了部分题目,更多Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等已整理上传,感兴趣的朋友可以看看支持一波!
程优先级**
1. 概述
一线互联网大厂Java核心面试题库
[外链图片转存中…(img-CDoA6mqM-1714321433253)]
正逢面试跳槽季,给大家整理了大厂问到的一些面试真题,由于文章长度限制,只给大家展示了部分题目,更多Java基础、异常、集合、并发编程、JVM、Spring全家桶、MyBatis、Redis、数据库、中间件MQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty等等已整理上传,感兴趣的朋友可以看看支持一波!