javaEE 多线程3

📅 发布时间:2026/10/16 8:20:36
javaEE 多线程3
线程安全性问题主要源于原子性、可见性和有序性。a等操作非原子多线程并发修改共享变量会导致结果不一致。锁如synchronized可保证同一时刻仅一个线程访问临界区避免竞争。但不当使用易引发死锁如嵌套锁或锁顺序不一致。内存可见性问题因线程缓存导致主内存修改不可见需用volatile关键字确保即时读取。指令重排序也可能影响正确性。通过合理使用锁、volatile及避免锁嵌套可有效保障线程安全。线程的安全性问题1锁互相争夺public class Thread3 { public static int a0; public static void main(String[] args) throws InterruptedException { Thread a1new Thread(()-{ for (int i 0; i 10000; i) { a; } }); Thread a2new Thread(()-{ for (int i 0; i 10000; i) { a; } }); a1.start(); a2.start(); a1.join(); a2.join(); System.out.println(a); } }在计算机a有3步指令操作1把a的值加载到CPU的寄存器上2CPU的寄存器进行3把寄存器的值返回给内存因为线程的随机调度有可能在a1线程执行3条指令的过程 就切换到另外一条线程中 又重新执行这个指令 但是第一次值没有返回回去所以带来了线程的安全性问题对于针对不同线程更改同一变量 必须等3条指令都执行完 才切换成下一线程 a的操作必须返回给a所在的内存中解决代码public class Thread3 { public static int a0; public static void main(String[] args) throws InterruptedException { Thread a1new Thread(()-{ for (int i 0; i 10000; i) { a; } }); Thread a2new Thread(()-{ for (int i 0; i 10000; i) { a; } }); a1.start(); //等待a1执行完 a1.join(); a2.start(); //等待a2执行完 a2.join(); System.out.println(a); } }线程的安全性1锁竞争问题public class Thread3 { //酱油 public static Object anew Object(); //醋 public static Object bnew Object(); public static void main(String[] args) { //锁竞争问题 Thread a1new Thread(()-{ synchronized (b){ System.out.println(a1拿醋); synchronized (a){ System.out.println(a1拿酱油); } } System.out.println(a1两个都拿到); }); Thread a2new Thread(()-{ synchronized (a){ System.out.println(a2拿酱油); synchronized (b){ System.out.println(a2拿醋); } } System.out.println(a2两个都拿到); }); a1.start(); a2.start(); }2不同线程对同一变量进行修改不同的线程对同一变量进行修改 但是线程的随机切换问题 出现锁的安全问题但是可以不同线程进行读操作 因为同一进程之间的线程的资源是共享的public class Thread3 { public static int a1000; public static void main(String[] args) { //读操作 Thread a1new Thread(()-{ System.out.println(a); }); Thread a2new Thread(()-{ System.out.println(a); }); a1.start(); a2.start(); }3原子性问题不可再分 一段代码必须完全执行完4内存可见性一个线程改完其他线程读到缓存里的旧值4循环嵌套5指令重排序计算机内部的指令被打乱顺序public static int a0; // public static void main(String[] args) throws InterruptedException { // Thread a1new Thread(()-{ // for (int i 0; i 10000; i) { // a; // } // }); // // Thread a2new Thread(()-{ // for (int i 0; i 10000; i) { // a; // } // }); // a1.start(); //a1.join(); // a2.start(); // a2.join(); // System.out.println(a);出现的安全问题 内存可见性 指令重排序 原子性问题 不同线程对同一变量进行修改于是引入了锁锁synchronized{//入锁}//出锁锁在不同地方的用法修饰普通方法是this加锁 给当前对象修饰静态的是类对象死锁一个锁对象在执行 另一个锁想要这个对象 但是此时这个锁在执行 对象在阻塞状态死锁在java官方怎么解释每个线程都有一个工作内存 共享一个主内存当一个线程进行读操作时 把主内存在复制一份到工作内存 进行读写后续另一个线程进行 先进行修改工作内存 感受不到主内存 值没有更新 所以出现死锁1可重入锁一个对象可加锁多次public static Object onew Object(); public static void main(String[] args) { Thread anew Thread(()-{ synchronized (o){ //入锁 synchronized (o){ 、//不阻塞之间进入 System.out.println(hello); } // } //出锁 }); a.start(); } 可重入锁针对同一线程 加多次锁当后续还有线程加锁看当前线程和前面线程是否相同重入锁的模拟使用//获取当前线程的引用 public Thread o; //记录锁次数 public int a0; //记录入锁的次数 public static int q1; //记录解锁次数 public static int w1; //第一次入锁 public synchronized void b() throws InterruptedException { Thread eThread.currentThread(); System.out.println(第q入锁次); if(a0){ oe; a; q; }else if(eo){ a; q; }else { this.wait(); b(); } } public synchronized void d(){ Thread eThread.currentThread(); if(e!o){ System.out.println(不是当前线程); } a--; System.out.println(第w次解锁); w; if(wq){ System.out.println(解锁成功); } if(a0){ onull; this.notify(); } } public static void main(String[] args) throws InterruptedException { Thread3 rnew Thread3(); r.b(); r.b(); r.d(); r.d(); }n个线程m把锁 哲学家就餐问题一个线程不能同时进行保持和请求的操作//获取当前线程的引用 public Thread o; //记录锁次数 public int a0; //记录入锁的次数 public static int q1; //记录解锁次数 public static int w1; //第一次入锁 public synchronized void b() throws InterruptedException { Thread eThread.currentThread(); System.out.println(第q入锁次); if(a0){ oe; a; q; }else if(eo){ a; q; }else { this.wait(); b(); } } public synchronized void d(){ Thread eThread.currentThread(); if(e!o){ System.out.println(不是当前线程); } a--; System.out.println(第w次解锁); w; if(wq){ System.out.println(解锁成功); } if(a0){ onull; this.notify(); } } public static void main(String[] args) throws InterruptedException { Thread3 rnew Thread3(); r.b(); r.b(); r.d(); r.d(); }n个线程m把锁 哲学家就餐问题有5个人就餐 只有4根筷子每个线程获得两根筷子 那按什么顺序获取呢按照获取最小位置和最大位置的筷子 用完释放锁的争夺死锁的经典场景1一个线程一个把锁 发生阻塞等待2两个线程两把锁 发生争夺3哲学家就餐问题死锁的发生条件1发生争夺 锁之间拿到一个锁之后 没有解锁 想要拿另外一个锁 阻塞等待2互斥拿到一个锁之后 没有解锁 想要拿另外一个锁3请求和保持一个锁保持锁 又想要申请另一个锁两个同时执行4循环等待至少要遵循2点如何避免死锁1避免嵌套锁 写并列锁2对锁的顺序做出约定‘线程安全类线程安全的内存可见性问题new Thread(()-{ while(!flag){ // 空循环 } System.out.println(循环退出); }).start(); Thread.sleep(1000); flag true; //主线程修改flagJVM 优化子线程第一次读到 flagfalse 放进缓存之后循环一直读缓存副本不再去主内存看最新 flag。主线程把 flag 改成 true 写到主内存但是子线程看不见。解决方法在变量前加volatile可以随时读取主内存的值