🌈个人主页:SKY-30
⛅个人推荐:基于java提供的ArrayList实现的扑克牌游戏 |C贪吃蛇详解
⚡学好数据结构,刷题刻不容缓:点击一起刷题
🌙心灵鸡汤:总有人要赢,为什么不能是我呢
🌈🌈🌈Java的多线程
⚡⚡⚡多线程的背景
为了充分利用cpu的多核心特性,我们引进了一种新的编程形式,并发编程,当然进程也可以进行并发编程,但是进程太"重"了,我们每次创建一个进程需要耗费很大的资源,对于有些业务场景可能需要对进程频繁的创建和删除,所以我们又引进了一个更轻量的进程-线程.
⚡⚡⚡多线程和多进程的联系
线程包含在进程之中,一个进程中至少包含一个或多个线程,对于线程来说它比进程创建和销毁的成本更小,并且一个进程中的多个线程是公用一份系统资源的(硬盘,网络带宽,内存…).
进程是操作系统资源分配的基本单位
线程是操作系统调度执行的基本单位
🌈🌈🌈在Java中使用多线程
==在Java中我们使用Java标准库中的Thread对象实现多线程.==对于实现Java的多线程我们这里有多种方式,这里给大家全部列举出来.
🌈🌈🌈创建线程
⚡⚡⚡⚡实现Thread类
// 定义一个Thread类,相当于一个线程的模板
class MyThread01 extends Thread {
// 重写run方法// run方法描述的是线程要执行的具体任务
@Overridepublic void run() {
System.out.println("hello, thread.");
}
}
/**
* 继承Thread类并重写run方法创建一个线程
*
*
*/
public class Thread_demo01 {
public static void main(String[] args) {
// 实例化一个线程对象
MyThread01 t = new MyThread01();
// 真正的去申请系统线程,参与CPU调度
t.start();
}
}
⚡⚡⚡实现Runnable接口
// 创建一个Runnable的实现类,并实现run方法
// Runnable主要描述的是线程的任务
class MyRunnable01 implements Runnable {
@Overridepublic void run() {
System.out.println("hello, thread.");
}
}
/**
* 通过继承Runnable接口并实现run方法
*
*
*/
public class Thread_demo02 {
public static void main(String[] args) {
// 实例化Runnable对象
MyRunnable01 runnable01 = new MyRunnable01();
// 实例化线程对象并绑定任务
Thread t = new Thread(runnable01);
// 真正的去申请系统线程参与CPU调度
t.start();
}
}
⚡⚡⚡使用匿名内部类实现线程
/**
* 通过Thread匿名内部类的方法创建一个线程
*
*
*/
public class Thread_demo03 {public static void main(String[] args) {
Thread t = new Thread(){
// 指定线程任务
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
};
// 真正的申请系统线程参与CPU调度
t.start();
}
}
⚡⚡⚡通过Runnable匿名内部类创建一个线程
**
* 通过Runnable匿名内部类创建一个线程
*
*
*/
public class Thread_demo04 {public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
// 指定线程的任务
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
// 申请系统线程参与CPU调度
t.start();
}
}
⚡⚡⚡利用lambda表达式
所以这里我们也可以使用lambda表达式实现一个线程
/**
* 通过Lambda表达式的方式创建一个线程
*/
public class Thread_demo05 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
// 指定任务:任务是循环打印当前线程名
while (true) {
System.out.println(Thread.currentThread().getName());
try {
// 休眠1000ms
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 申请系统线程参与CPU调度
t.start();
}
}
🌈🌈🌈Java中多线程的重要属性
这里强调一下==isDaemon()==方法,可以判定是否为后台线程即后台线程不会影响进程的运行,而前台线程结束时会关闭当前的进程的运行
.
还有就是getState()获取当前线程的状态,一种有六种状态,这个我们后面再说
🌈🌈🌈启动线程
run方法和start方法有什么区别???
特别说明:一个线程,只能调用一次start方法
🌈🌈🌈线程终止
对于一个线程来说,要想终止,只能加快run方法的执行,将run方法结束,来结束线程.这里我们利用interrupt方法终止线程,并且这个方法还会设置一个标志位.
public class ThreadInterruptExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Thread is running...");
Thread.sleep(1000); // 模拟一些工作
}
} catch (InterruptedException e) {
System.out.println("Thread was interrupted!");
}
System.out.println("Thread is exiting.");
});
thread.start(); // 启动线程
try {
Thread.sleep(2500); // 等待2.5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt(); // 请求中断线程
}
}
这里就是在main方法中通过interrupt方法终止线程thread,然后开始执行main中的剩余代码.
特别说明interrupt方法执行之后,还会将sleep的线程唤醒,并且清空标志位
public class ThreadInterruptExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
while (true) {
System.out.println("Thread is running...");
Thread.sleep(1000); // 模拟阻塞状态
}
} catch (InterruptedException e) {
System.out.println("Thread was interrupted!");
return; // 退出线程
}
});
thread.start(); // 启动线程
try {
Thread.sleep(2500); // 主线程等待2.5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt(); // 请求中断线程
}
}
⚡⚡⚡线程等待
public class Main {
public static void main(String[] args) {
Thread worker = new Thread(new Runnable() {
public void run() {
System.out.println("Worker thread is running.");
try {
Thread.sleep(2000); // 模拟工作线程执行任务需要2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Worker thread has finished.");
}
});
worker.start(); // 启动工作线程
try {
worker.join(); // 主线程等待工作线程完成
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread is continuing after worker thread has finished.");
}
}
⚡⚡⚡join()方法
对于这个方法来说,它可以决定线程的等待状态,然而,如果被等待线程如果出现了一些bug,那么等待线程就会一直处于等待状态,即"死等状态",但是显然这种状态并不是我们想要的,所以join方法就给我们提供了两种版本,有参数和无参数版本,无参数即可能陷入死等状态,有参数即可以设置等待的时间单位为毫秒,时间结束不管被等待线程有没有执行完毕,都会结束这个join方法.
🌈🌈🌈线程的状态
对于进程来说,大致分为两种状态,就绪状态和阻塞状态.
就绪状态:正在运行,或者随时可以调到CPU上执行.
阻塞状态:线程处于等待状态,不能立刻执行
然而对于线程来说,他的状态就远远不止这两种了~~
线程一共有六种状态,这里给大家列举出来
NEW
public class Test {
public static void main(String[] args) {
Thread worker = new Thread(new Runnable() {
public void run() {
System.out.println("Worker thread is running.");
try {
Thread.sleep(2000); // 模拟工作线程执行任务需要2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Worker thread has finished.");
}
});
System.out.println( worker.getState());
worker.start(); // 启动工作线程
try {
worker.join(); // 主线程等待工作线程完成
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread is continuing after worker thread has finished.");
}
}
运行结果:
⚡⚡⚡TERMINATED
public class Main {
public static void main(String[] args) {
Thread worker = new Thread(new Runnable() {
public void run() {
System.out.println("Worker thread is running.");
try {
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新设置中断状态
}
System.out.println("Worker thread has finished.");
}
});
worker.start(); // 启动线程
while (!worker.getState().equals(Thread.State.TERMINATED)) {
System.out.println("Main thread is waiting for worker thread to finish.");
try {
Thread.sleep(1000); // 每1秒检查一次
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新设置中断状态
}
}
System.out.println("Worker thread is in TERMINATED state.");
}
}
⚡⚡⚡BLOCKED
这是由于锁竞争导致的阻塞,这里我们还没有引入锁所以先跳过
⚡⚡⚡RUNNABLE
public class Test {
public static void main(String[] args) {
Thread worker = new Thread(new Runnable() {
public void run() {
System.out.println( Thread.currentThread().getState());
System.out.println("Worker thread is running.");
try {
Thread.sleep(2000); // 模拟工作线程执行任务需要2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Worker thread has finished.");
}
});
//System.out.println( worker.getState());
worker.start(); // 启动工作线程
try {
worker.join(); // 主线程等待工作线程完成
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Main thread is continuing after worker thread has finished.");
}
}
运行结果
⚡⚡⚡TIMED_WAITING和WAITING
这两种状态其实就是我们上面提到的一个是处于有时间的暂时等待,另一个则是无限制的死等,即可以利用join的两个版本实现这两个状态.对于有时间的,也可以用sleep检验…
🌈🌈🌈🌈线程安全
public class MyThreadDemo1 {
public static int count=0;
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(()->{
for(int i=0;i<50000;i++){
count++;
}
});
Thread t2=new Thread(()->{
for(int i=0;i<50000;i++){
count++;
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
}
这里运行出来的结果就并非是100000,原因是内存中的count值没有及时更新
然而对于多线程来说,随时都可能被其他线程占据cpu的运行资源,由于cpu的最小执行单位是指令,所以起码每次执行终止都会让当前的指令执行完毕.
这样的机制就会导致线程安全问题,下面给大家画图演示一下.
此时就会出现t2虽然count值已经加一,但是没有及时更新内存(方块
)中的count值,所以导致后面t1加载count时还是0所以后面的count的值就是1