当前位置: 首页 > news >正文

JavaEE初阶——从入门到掌握线程安全

在这里插入图片描述
在这里插入图片描述
—JAVAEE— ⬅(click)


Java多线程编程初阶:从入门到掌握线程安全

1. 认识线程(Thread)

线程是什么

线程是程序中的执行流,多个线程可以并发执行多个任务。例如,一家公司办理银行业务,多个员工分别处理转账、发福利、缴社保,这就是多线程的典型场景。

进程和线程的区别

  • 进程是包含线程的.每个进程⾄少有⼀个线程存在,即主线程。
  • 进程和进程之间不共享内存空间.同⼀个进程的线程之间共享同⼀个内存空间.
  • 进程是系统分配资源的最⼩单位,线程是系统调度的最小单位。
  • ⼀个进程挂了⼀般不会影响到其他进程.但是⼀个线程挂了,可能把同进程内的其他线程⼀起带走(整个进程崩溃).
  • 在这里插入图片描述

创建线程的几种方式

方式示例代码
继承Thread类class MyThread extends Thread { public void run() { ... } }
实现Runnable接口class MyRunnable implements Runnable { public void run() { ... } }
匿名内部类new Thread(() -> { ... }).start();
Lambda表达式new Thread(() -> System.out.println("Hello")).start();

使用重写Thread类创建一个多线程程序

import java.util.Random;public class ThreadDemo {private static class MyThread extends Thread {@Overridepublic void run() {Random random = new Random();while (true) {System.out.println(Thread.currentThread().getName());try {Thread.sleep(random.nextInt(10));} catch (InterruptedException e) {e.printStackTrace();}}}}public static void main(String[] args) {MyThread t1 = new MyThread();t1.start();Random random = new Random();while (true) {System.out.println(Thread.currentThread().getName());try {Thread.sleep(random.nextInt(10));} catch (InterruptedException e) {e.printStackTrace();}}}
}

使用lambda表达式创建一个多线程程序

package Thread;public class Demo5 {public static void main(String[] args) {// 使用lambda表达式Thread thread1 = new Thread(() -> {while (true) {System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}, "thread-1");thread1.start();}
}

使用 jconsole 命令观察线程

在这里插入图片描述

多线程的优势

多线程能充分利用多核CPU,提高程序运行效率。以下是一个对比串行与并发执行的示例:

public class ThreadAdvantage {private static final long count = 10_0000_0000;public static void main(String[] args) throws InterruptedException {concurrency();serial();}private static void concurrency() throws InterruptedException {long begin = System.nanoTime();Thread thread = new Thread(() -> {int a = 0;for (long i = 0; i < count; i++) a--;});thread.start();int b = 0;for (long i = 0; i < count; i++) b--;thread.join();long end = System.nanoTime();double ms = (end - begin) * 1.0 / 1000 / 1000;System.out.printf("并发: %f 毫秒%n", ms);}private static void serial() {long begin = System.nanoTime();int a = 0;for (long i = 0; i < count; i++) a--;int b = 0;for (long i = 0; i < count; i++) b--;long end = System.nanoTime();double ms = (end - begin) * 1.0 / 1000 / 1000;System.out.printf("串行: %f 毫秒%n", ms);}
}

2. Thread类及常见方法

构造方法

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程
Thread(String name)创建命名线程
Thread(Runnable target, String name)使用Runnable创建命名线程

常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

2.3 启动线程:start()

Thread t = new Thread(() -> System.out.println("线程运行"));
t.start(); // 真正启动线程

中断线程

方式一:自定义标志位
public class ThreadDemo {private static class MyRunnable implements Runnable {public volatile boolean isQuit = false;@Overridepublic void run() {while (!isQuit) {System.out.println("转账中...");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("停止转账");}}public static void main(String[] args) throws InterruptedException {MyRunnable target = new MyRunnable();Thread t = new Thread(target, "李四");t.start();Thread.sleep(5000);target.isQuit = true;}
}
方式二:使用interrupt()
package Thread;//中断线程
//通过调用线程的interrupt()方法,来中断线程
public class Demo8 {public static void main(String[] args) throws InterruptedException {// 注意,此处针对lambda的定义其实是在new Thread之前的!// 所以如果在lambda中使用thread,是会报错的!Thread thread = new Thread(() -> {// currentThread()是Thread提供的一个静态方法// 它的功能是哪个线程调用这个方法,就返回哪个线程对象的引用while (!Thread.currentThread().isInterrupted()) {// 由于这个 currentThread()方法,是在后续thread.start 之后,才执行的.// 并且是在thread线程中执行的。返回的结果就是指向thread线程对象的引用了System.out.println("hello thread");try {Thread.sleep(1000);} catch (InterruptedException e) {// 如果线程在sleep状态时,被中断了,会触发InterruptedException异常// 当线程thread正在sleep时,主线程(main)调用thread的interrupt方法,可以提前唤醒sleep状态的线程,此时会触发InterruptedException异常。通过这个异常,能区分线程是正常休眠结束还是被提前中断唤醒。并且,当sleep因提前唤醒触发异常后,会将线程的中断标志位(isInterrupted)重置为false。因此会无限循环!// 这个printStackTrace()方法,只是用来打印异常的栈轨迹的。e.printStackTrace();// 当线程在sleep状态时被中断,会抛出InterruptedException异常。我们可以在catch块中处理这个异常,例如记录日志、清理资源等。同时,我们可以根据业务需求,选择是否继续循环或退出线程或稍等一会儿在退出循环。这里我们选择退出线程// 添加break之后,触发异常时就会结束循环,让线程结束// 在break之前也可以添加一些其他善后逻辑,相当于稍后再结束// doSomething();break;// 使用IDEA自动生成catch语句,此时默认给的代码就是再次抛出一个其他异常,如RuntimeException,但是这个做法太粗暴了,它不只是让thread线程结束,也会使整个进程结束,因为没有人catch这个RuntimeException异常。不推荐// throw new RuntimeException(e);}}});thread.start();// 在main线程中尝试终止thread线程Thread.sleep(3000);thread.interrupt();}
}

等待线程:join()

public class JoinDemo {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {for (int i = 0; i < 5; i++) {System.out.println("t1运行中");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t1.start();t1.join(); // 等待t1结束System.out.println("t1已结束");}
}

获取当前线程引用

Thread currentThread = Thread.currentThread();
System.out.println(currentThread.getName());

休眠线程:sleep()

Thread.sleep(1000); // 休眠1秒

3. 线程的状态和转移

在这里插入图片描述

线程状态枚举

public class ThreadState {public static void main(String[] args) {for (Thread.State state : Thread.State.values()) {System.out.println(state);}}
}

状态包括:NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED

状态转移图

[NEW]|| start()v
[RUNNABLE] <----------------+|                        || 获取CPU                 | yield()/时间片用完v                        |
[RUNNING] ------------------+|| sleep()/wait()/join()v
[TIMED_WAITING/WAITING]|| 时间到/notify()/interrupt()v
[RUNNABLE] <----------------+^                        ||                        | 获取锁| 等待锁                 |+------------------------+|                        |v                        |
[BLOCKED] ------------------+[RUNNING]|| 执行完毕v
[TERMINATED]

4. 线程安全与同步机制

线程不安全示例

public class ThreadUnsafe {private 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: " + count); // 可能小于100000}
}

线程不安全的原因

原因说明
原子性操作被中断导致数据不一致
可见性线程间数据更新不可见
指令重排序编译器/CPU优化导致执行顺序变化

synchronized关键字

修饰代码块
public class SynchronizedDemo {private final Object locker = new Object();private int count = 0;public void increment() {synchronized (locker) {count++;}}
}
修饰方法
public synchronized void increment() {count++;
}
修饰静态方法
public static synchronized void increment() {count++;
}

volatile关键字

public class VolatileDemo {static class Counter {public volatile int flag = 0;}public static void main(String[] args) {Counter counter = new Counter();Thread t1 = new Thread(() -> {while (counter.flag == 0) {// 空循环}System.out.println("循环结束");});Thread t2 = new Thread(() -> {Scanner scanner = new Scanner(System.in);System.out.println("输入整数:");counter.flag = scanner.nextInt();});t1.start();t2.start();}
}

5. 线程间协作:wait与notify

public class WaitNotifyDemo {static class WaitTask implements Runnable {private final Object locker;public WaitTask(Object locker) {this.locker = locker;}@Overridepublic void run() {synchronized (locker) {try {System.out.println("wait开始");locker.wait();System.out.println("wait结束");} catch (InterruptedException e) {e.printStackTrace();}}}}static class NotifyTask implements Runnable {private final Object locker;public NotifyTask(Object locker) {this.locker = locker;}@Overridepublic void run() {synchronized (locker) {System.out.println("notify开始");locker.notify();System.out.println("notify结束");}}}public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread t1 = new Thread(new WaitTask(locker));Thread t2 = new Thread(new NotifyTask(locker));t1.start();Thread.sleep(1000);t2.start();}
}

6. 多线程案例实战

单例模式

懒汉式(线程安全)
public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

阻塞队列

public class BlockingQueue {private final int[] items;private int head = 0;private int tail = 0;private int size = 0;public BlockingQueue(int capacity) {items = new int[capacity];}public synchronized void put(int value) throws InterruptedException {while (size == items.length) {wait();}items[tail] = value;tail = (tail + 1) % items.length;size++;notifyAll();}public synchronized int take() throws InterruptedException {while (size == 0) {wait();}int value = items[head];head = (head + 1) % items.length;size--;notifyAll();return value;}
}

定时器

定时器是什么
定时器也是软件开发中的⼀个重要组件.类似于⼀个"闹钟".达到⼀个设定的时间之后,就执行某个指定好的代码
在这里插入图片描述

public class MyTimer {private final PriorityQueue<MyTask> queue = new PriorityQueue<>();private final Object locker = new Object();public void schedule(Runnable task, long delay) {synchronized (locker) {queue.offer(new MyTask(task, System.currentTimeMillis() + delay));locker.notify();}}public MyTimer() {Thread worker = new Thread(() -> {while (true) {synchronized (locker) {while (queue.isEmpty()) {try {locker.wait();} catch (InterruptedException e) {e.printStackTrace();}}MyTask task = queue.peek();long currentTime = System.currentTimeMillis();if (currentTime >= task.time) {queue.poll();task.runnable.run();} else {try {locker.wait(task.time - currentTime);} catch (InterruptedException e) {e.printStackTrace();}}}}});worker.start();}static class MyTask implements Comparable<MyTask> {Runnable runnable;long time;public MyTask(Runnable runnable, long time) {this.runnable = runnable;this.time = time;}@Overridepublic int compareTo(MyTask o) {return Long.compare(time, o.time);}}
}

线程池

public class MyThreadPool {private final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();public MyThreadPool(int nThreads) {for (int i = 0; i < nThreads; i++) {new Thread(() -> {while (true) {try {Runnable task = queue.take();task.run();} catch (InterruptedException e) {e.printStackTrace();}}}).start();}}public void submit(Runnable task) {try {queue.put(task);} catch (InterruptedException e) {e.printStackTrace();}}
}

7. 总结与对比

进程 vs 线程

对比项进程线程
资源分配独立内存空间共享进程资源
创建开销
切换开销
通信方式复杂(IPC)简单(共享内存)

线程安全保证思路

  1. 无共享资源:避免共享数据
  2. 只读共享:使用不可变对象
  3. 同步机制
    • 原子性:synchronized
    • 可见性:volatile
    • 有序性:避免指令重排序

本文详细介绍了Java多线程编程的基础知识、常见问题及解决方案,并提供了丰富的代码示例。掌握多线程编程是Java工程师的必备技能,希望本文能帮助你更好地理解和应用多线程技术。

欢迎在评论区交流讨论!

http://www.dtcms.com/a/392552.html

相关文章:

  • GitHub热门大数据项目:基于人体生理指标管理的可视化分析系统技术解析
  • 零基础学Docker(2)--基本命令
  • 华为FusionCloud私有云:企业数字化转型的智能底座
  • 【LVS入门宝典】LVS NAT模式深度解析:从原理到实战配置指南
  • MQ 项目(实习项目,初步更新)
  • Redis中Lua脚本的应用场景分析
  • phpkg 让 PHP 摆脱 Composer 依赖地狱
  • Python -- 人生重开模拟器(简易版)
  • CSS基础查缺补漏(持续更新补充)
  • 用户生命周期价值(CLV)目标变量系统性设计与实践(二)
  • TDengine 与工业应用平台 Ignition 集成
  • JVM垃圾收集中判断对象存活相关问题
  • 【C++】告别“类型转换”踩坑,从基础到四种核心强制转换方式
  • WinDivert学习文档之五-————编程API(八)
  • 【LVS入门宝典】LVS NAT模式深度解析:流量走向与IP包头修改机制
  • 第二章 微调:定制专属模型——从通用能力到场景适配
  • 为统信UOS2.0离线安装python3.11.9开发环境
  • Maven 进阶:依赖管理的 “坑” 与解决方案
  • 2.15Vue全家桶-VueRouter
  • 五、Maven引入
  • 通过 TypeScript 在 Vue 3 中利用类型系统优化响应式变量的性能
  • Maven 入门:从 “手动导包” 到 “自动化构建” 的第一步
  • 【Python】数组
  • AI任务相关解决方案18-基于大模型、MCP、Agent与RAG技术的数据分析系统研究报告
  • 飞牛NAS系统版本重大更新:支持挂载115网盘!挂载教程来袭!
  • SpringAI、Dify与Ollama的技术落地与协作
  • Python Selenium 核心技巧与实战:从基础操作到极验滑动验证码破解
  • PyQt6 实战:多源输入 ASCII 艺术转换器全解析(图片 / 视频 / 摄像头实时处理 + 自定义配置)
  • Java 大视界 —— Java 大数据在智能农业病虫害精准识别与绿色防控中的创新应用
  • Qt qDebug()调试函数,10分钟讲清楚