ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

多线程初级

多线程初级 什么是线程Thread线程Thread是操作系统能够进行调度的最小执行单元。可以把它理解为程序内部的一条独立执行路径通俗理解银行办业务假设一家公司要去银行办理业务既要财务转账又要福利发放还得缴社保单线程模式只有张三一个会计他需要一个人依次完成三件事耗时很长。多线程模式张三叫来李四、王五一起帮忙。三个人分别负责一件事各自排队办理。三个人都是独立的“执行流”共同为这家公司服务。张三就是主线程Main Thread李四、王五就是新创建的子线程三个执行流并发执行提高了整体效率为什么需要多线程① 充分利用多核CPU单核CPU的发展已遇瓶颈现在都是多核CPU。多线程能充分利用多核资源提高程序运行效率。② 处理IO等待当程序需要等待IO操作如网络请求、文件读写时CPU可以切换到其他线程执行任务避免CPU空闲等待。③线程比进程更轻量调度开销和资源占用都更少。线程和进程的区别进程是包含线程的每个进程最少有一个线程存在即主线程。进程和进程之间补共不内存空间同一个进程的线程之间共享内存空间。进程是系统分配资源的最小空间线程是系统调度的最小单位。Java线程和操作系统线程的关系线程是操作系统的概念。操作系统内核实现了线程机制并提供API供用户使用如Linux的pthread库。Java中的Thread类是对操作系统线程API的进一步抽象和封装让我们可以跨平台地使用多线程。创建线程的四种方法1.继承Thread类来创建线程类2.实现Runnable接口3.匿名内部类创建Thread子类对象(或Runnable子类对象4.lambda表达式创建Runnable子类对象注意Lambda表达式要求接口是函数式接口只有一个抽象方法Runnable正好满足。多线程的优势提升运行速度Thread类核心方法详解1.常见构造方法2.常见属性3.启动线程Thread t new MyThread(); t.start(); // ✅ 在操作系统创建新线程并执行run()4.中断线程一般有两种方法用共享的标记进行沟通添加volatile关键字public class ThreadDemo { private static class MyRunnable implements Runnable { public volatile boolean isQuit false; // volatile保证可见性 Override public 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 thread new Thread(target); thread.start(); Thread.sleep(5000); // 5秒后通知停止 target.isQuit true; // 修改标志位 } }第二种是使用interrupt方法public class ThreadDemo { private static class MyRunnable implements Runnable { Override public void run() { while (!Thread.currentThread().isInterrupted()) { System.out.println(线程工作中...); try { Thread.sleep(1000); } catch (InterruptedException e) { // sleep被中断清除中断标志需要手动退出 System.out.println(收到中断信号退出); break; } } System.out.println(线程结束); } } public static void main(String[] args) throws InterruptedException { MyRunnable target new MyRunnable(); Thread thread new Thread(target); thread.start(); Thread.sleep(5000); thread.interrupt(); // 线程对象调用interrupt方法发送中断信号 } }两种中断方式的区别5.等待线程结束当主线程需要等待子线程执行完毕后再继续时使用join()。public class ThreadDemo { public static void main(String[] args) throws InterruptedException { Runnable target () - { for (int i 0; i 5; i) { System.out.println(Thread.currentThread().getName() 工作中...); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName() 结束); }; Thread t1 new Thread(target, 李四); Thread t2 new Thread(target, 王五); System.out.println(先让李四工作); t1.start(); t1.join(); // 主线程等待李四完成 System.out.println(李四结束了再让王五工作); t2.start(); t2.join(); // 主线程等待王五完成 System.out.println(都结束了); } }6.获取当前线程引用Thread.currentThread()Thread current Thread.currentThread(); System.out.println(当前线程名 current.getName());7.线程休眠sleep()// 休眠3秒 Thread.sleep(3000); // 更精确的休眠纳秒级但实际精度取决于操作系统 Thread.sleep(3000, 500000);注意sleep()只能保证休眠时间不少于指定时间实际唤醒时间取决于操作系统的线程调度。线程状态线程的状态是⼀个枚举类型Thread.StateNEW:安排了⼯作,还未开始⾏动RUNNABLE:可⼯作的⼜可以分成正在⼯作中和即将开始⼯作.BLOCKED:这⼏个都表⽰排队等着其他事情• WAITING TIMED_WAITINGTERMINATED:⼯作完成了.状态转移线程安全如果多线程环境下代码运⾏的结果是符合我们预期的即在单线程环境应该的结果则说这个程序是 线程安全的。① 线程调度是抢占式的随机性操作系统调度线程的顺序是不确定的程序员必须保证在任意执行顺序下代码都能正常工作。② 多个线程修改共享数据上面的例子中count是多个线程共享的变量。多个线程同时修改它会导致数据不一致。③ 操作不具备原子性count; //看起来是一行代码实际是三步操作从内存读取count的值到CPU寄存器CPU对值进行1运算将结果写回内存如果线程A执行完第1步后被切换线程B修改了count然后线程A继续执行第2、3步就会覆盖B的修改导致数据丢失Java内存模型JMM⽬的是屏蔽掉各种硬件和操作系统的内存访问差异以实现让Java程序在各种平台下都能达到⼀致的 并发效果.线程之间的共享变量存在 主内存 (Main Memory).每⼀个线程都有⾃⼰的 ⼯作内存 (Working Memory).当线程要读取⼀个共享变量的时候,会先把变量从主内存拷⻉到⼯作内存,再从⼯作内存读取数据.当线程要修改⼀个共享变量的时候,也会先修改⼯作内存中的副本,再同步回主内存synchronized详解synchronized特性1.互斥性synchronized会起到互斥效果,某个线程执⾏到某个对象的synchronized中时,其他线程如果也执⾏ 到同⼀个对象synchronized就会阻塞等待.进⼊synchronized修饰的代码块,相当于加锁退出synchronized修饰的代码块,相当于解锁类比厕所门上的“有人/无人”标识无人 → 可以进入设置为“有人”有人 → 其他人只能在门外排队等待public class ThreadSafe { private static int count 0; private static final Object locker new Object(); public static void main(String[] args) throws InterruptedException { Thread t1 new Thread(() - { for (int i 0; i 50000; i) { synchronized (locker) { count; } } }); Thread t2 new Thread(() - { for (int i 0; i 50000; i) { synchronized (locker) { count; } } }); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println(count: count); // 稳定输出100000 } }2.可重入性同一个线程可以多次获取同一把锁不会死锁。synchronized (locker) { synchronized (locker) { // 同一线程再次获取锁允许 count; } }在可重⼊锁的内部,包含了线程持有者和计数器两个信息.实现原理每个锁内部记录持有线程和计数器加锁时如果是同一个线程计数器1解锁时计数器-1减到0时才真正释放锁public class SynchronizedDemo { private final Object locker new Object(); // 1. 锁指定对象 public void method1() { synchronized (locker) { // 业务代码 } } // 2. 锁当前对象 (this) public synchronized void method2() { // 业务代码 } // 3. 锁类的Class对象 public synchronized static void method3() { // 业务代码 } }线程安全类String是不可变类本身线程安全StringBuffer内部方法加了synchronized但已不推荐使用单线程用StringBuilder多线程用其他方式同步volatile关键字volatile修饰的变量,能够保证内存可⻅性.前⾯我们讨论内存可⻅性时说了,直接访问⼯作内存(实际是CPU的寄存器或者CPU的缓存),速度⾮ 常快,但是可能出现数据不⼀致的情况.volatile修饰的变量读写操作直接在主内存进行不经过工作内存CPU缓存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(); // 用户输入非0时t1结束 }); t1.start(); t2.start(); }如果flag不加volatilet1可能永远看不到t2对flag的修改导致死循环。加上volatile后t1每次都能读到主内存中的最新值。wait()和notify()线程间的通信wait()的作用释放当前锁让出CPU线程进入等待状态其他线程调用notify()或notifyAll()后被唤醒wait要搭配synchronized来使⽤.脱离synchronized使⽤wait会直接抛出异常.notify()的作用唤醒一个正在等待该锁的线程被唤醒的线程需要重新竞争锁public class WaitNotifyDemo { public static void main(String[] args) throws InterruptedException { Object locker new Object(); // 等待线程 Thread t1 new Thread(() - { synchronized (locker) { try { System.out.println(等待中...); locker.wait(); // 释放锁进入等待 System.out.println(被唤醒); } catch (InterruptedException e) { e.printStackTrace(); } } }); // 唤醒线程 Thread t2 new Thread(() - { synchronized (locker) { System.out.println(准备唤醒); locker.notify(); // 唤醒等待中的线程 System.out.println(已唤醒); } }); t1.start(); Thread.sleep(1000); t2.start(); } }notify()随机唤醒一个等待线程notifyAll()唤醒所有等待线程它们需要竞争锁
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表