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

再探多线程Ⅰ--- (创建思路+核心方法+代码样例)

   上篇我们详细的拆解了多线程的基本概念及架构 , 我希望通过本篇文章系统性的介绍并展示多线程的创建思路, 常用方法及线程安全等补充, 我们从温故线程的基本状态开始!

  • 1. 线程状态

    • 1.1 线程状态总览

    • 我绘制了以下流程图作为上一篇的总结
    • 1.2 核心要点

      • 1.2.1 sleep(time) 和 wait(time) 有啥区别?

      • a. sleep(time): 线程睡眠, 睡眠过程中不会释放锁, 此时其他线程无法抢到锁, 设置的时间一旦超时, 自动醒来并继续执行
      • b. wait(time): 线程等待,等待过程中会释放锁, 其他线程就有可能抢到锁, 如果在等待期间被唤醒或者等待超时, 会和其他线程共同重新抢锁, 如果抢到了才能继续执行,抢不到则锁阻塞
      • 1.2.2 解释wait()和notify()?

      • a. 空参wait: wait() ,线程会进入到无限等待状态,会释放锁,需要其他同一锁下的线程调用notify(一次只能唤醒一条线程且是随机的)或者 使用notifyAll(将所有同一个对象锁下的等待线程全部唤醒), 被唤醒之后会和其他线程重新抢锁 ,抢到了继续执行,抢不到则阻塞
      • b. notify():一次只能唤醒一条线程且是随机的
      • c. notifyAll():将所有同一个对象锁下的等待线程全部唤醒
      • 1.2.3 wait()和notify()用法?

      • a. 两个方法都需要锁对象调用, 所以两个方法需要用到同步代码块(同步方法)
      • b. 两个方法的调用必须是同一个锁对象
  •  2. 创建多线程的方式

    • 2.1 继承Thread

      • a. 定义一个类并继承(extends)Thread
      • b. 重写run 方法并设置线程任务
      • c. 创建自定义线程类, 将其放到Thread对象之中
      • d. 调用start方法并开启线程,jvm自动执行run方法
      • 代码示例:
      • public class Test01 {public static void main(String[] args) {MyThread t1=new MyThread();//创建线程对象t1.start();//调用start开启线程,jvm自动调用run方法for(int i=0;i<3;i++){System.out.println("main线程--------"+i);}}
        }
        public class MyThread extends Thread{@Overridepublic void run() {for(int i=0;i<3;i++){System.out.println("my thread---"+i);}}
        }
      • 运行结果:

    • 2.2 实现Runnable

      • a. 定义一个类, 实现Runnable接口
      • b. 重写run方法, 设置线程任务
      • c. 创建自定义线程类并创建线程任务的对象, 将该对象其放到Thread对象中
      • d. 调用 start方法, 开启线程, jvm自动执行run方法
      • 代码示例:
      • public class MyRunnable implements Runnable{//创建线程的第二种方式-->实现接口@Overridepublic void run() {for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+"...执行了"+i);}}
        }
        
        public class Test01 {public static void main(String[] args) {MyRunnable myRunnable=new MyRunnable();Thread t1=new Thread(myRunnable);//调用Thread中的方法开启线程t1.start();for(int i=0;i<10;i++){System.out.println(Thread.currentThread().getName()+"...执行了"+i);}}
        }

        代码结果:

    • 2.3 匿名内部类(2.2的扩展)

      • public class Test02 {public static void main(String[] args) {Thread t2=new Thread(new Runnable(){//匿名内部类实现Runnable接口@Overridepublic void run() {for(int i=0;i<4;i++){System.out.println(Thread.currentThread().getName()+"...执行了"+i);}}});t2.setName("t2");t2.start();}
        }

        代码结果:

  • 3. Thread 中的方法

    • a. void start() -> 开启线程, jvm自动调用run方法
    • b. void run() -> 设置线程任务,这个run 方法是Thread重写的接口Runnable中的run方法
    • c. String getName() ->获取线程名字
    • d. void setName(String name) ->命名
    • e. static void sleep(time) -> 线程睡眠(超时后自动醒来继续执行,传递的是毫秒值)
    • f. staticThread currentThread() -> 获取当前正在执行中的线程
  • 4. 线程安全

    • 4.1 什么时候发生? (synchronized出场?)

      • a. 共享数据修改: 多个线程同时读写同一变量(如全局计数器、集合元素)
      • b. 非原子操作: 复合操作被线程切换打断(如 i++ 实际包含"读-改-写"三步)
      • c. 可见性问题: 线程A修改数据后,线程B看不到最新值, 
        • 原因:CPU缓存 vs 主内存不一致
        • d. 指令重排序问题:  编译器/CPU优化打乱代码顺序
      • // 风险点标记
        class ThreadUnsafeExample {// 风险1:共享可变数据private static int counter = 0; // 风险2:非线程安全集合private static List<String> dataList = new ArrayList<>();public void addData(String item) {// 风险3:非原子复合操作if (!dataList.contains(item)) {dataList.add(item);}}public void increment() {// 风险4:非原子自增counter++; }
        }
    • 4.2 同步代码块(Ⅰ)

      • synchronized(锁对象){可能出现线程安全问题的代码}
      • 代码示例:
      • public class MyTicket implements Runnable {int ticket = 1000;//定义100张票//任意new一个对象final Object obj=new Object();@Overridepublic void run() {//买票while (true) {try {//一定延时Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (obj) {//方法一:对于代码块(可能出现线程安全问题的)if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");ticket--;}}}}
        }
        public class Test01 {public static void main(String[] args) {MyTicket myTicket=new MyTicket();Thread t1=new Thread(myTicket,"马牛逼");Thread t2=new Thread(myTicket,"蔡徐坤");Thread t3=new Thread(myTicket,"鸽哥");t1.start();t2.start();t3.start();}
        }

        代码结果:

    • 4.3 同步方法

      • 4.3.1 非静态的同步方法

        • a. synchronized 返回值类型 方法名 (形参){方法体 返回值}
        • b. 默认锁: this(即当前创建的对象)
        • 代码示例:
        • public class MyTicket implements Runnable {int ticket = 1000;//定义100张票@Overridepublic void run() {//买票while (true) {try {//一定延时Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}
          //            method01();method02();}}//    public synchronized void method01(){//方法2:单独写一个非静态的synchronized方法独立于run方法(在run中调用即可)
          //        if (ticket > 0) {
          //            System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");
          //            ticket--;
          //        }
          //    }public void method02() {//方法3:单独写一个非静态的方法独立于run方法并在核心代码块外包裹synchronized(在run中调用即可)synchronized (this) {//默认锁:this(当前唯一new的对象)System.out.println(this);if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");ticket--;}}}
          }
          public class Test01 {public static void main(String[] args) {MyTicket myTicket=new MyTicket();System.out.println(myTicket);Thread t1=new Thread(myTicket,"马牛逼");Thread t2=new Thread(myTicket,"蔡徐坤");Thread t3=new Thread(myTicket,"鸽哥");t1.start();t2.start();t3.start();}
          }

          代码结果:

      • 4.3.2 静态的同步方法

        • a. 修饰符 static sychronized 返回值类型 方法名 (形参){方法体 返回值}
        • b. 默认锁: class 对象
        • 代码示例:
        • public class MyTicket implements Runnable {static int ticket = 1000;//@Overridepublic void run() {//具体操作while (true) {try {//延时Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}method03();}}public static void method03(){//方法4:静态同步方法synchronized (MyTicket.class) {//默认锁:当前类的class对象if (ticket > 0) {System.out.println(Thread.currentThread().getName() + "买了第" + ticket + "张票");ticket--;}}}}
          public class Test01 {public static void main(String[] args) {MyTicket myTicket=new MyTicket();System.out.println(myTicket);Thread t1=new Thread(myTicket,"马牛逼");Thread t2=new Thread(myTicket,"蔡徐坤");Thread t3=new Thread(myTicket,"鸽哥");t1.start();t2.start();t3.start();}
          }

          代码结果:

  • 5. 死锁

    • 5.1 死锁?

      • 两个或多个线程互相持有对方需要的资源,并无限等待对方释放,导致所有线程永久阻塞的状态。
    • 5.2 死锁发生的四个必要条件

      • a. 互斥: 资源只能被一个线程独占使用(如锁)
      • b. 持有并等待: 线程已持有资源,同时等待获取其他线程的资源
      • c. 不可剥夺: 资源只能由持有者主动释放,无法强行抢夺
      • d. 循环等待: 线程间形成环形等待链(T1 等待 T2 的资源 → T2 等待 T3 的资源 → ... → Tn 等待 T1 的资源)
    • 5.3 经典场景

      • 代码示例:
      • public class Deadlock implements Runnable {private final boolean flag;public Deadlock(boolean flag) {this.flag = flag;}@Overridepublic void run() {if (flag) {synchronized (LockA.locka) {System.out.println("if...locka");synchronized (LockB.lockb) {System.out.println("if...lockb");}}} else {synchronized (LockB.lockb) {System.out.println("else...lockb");synchronized (LockA.locka) {System.out.println("else...locka");}}}}
        }
        public class Test {public static void main(String[] args) {Deadlock deadlock1=new Deadlock(true);Deadlock deadlock2=new Deadlock(false);new Thread(deadlock1).start();new Thread(deadlock2).start();}
        }
        public class LockA {public final static LockA locka=new LockA();
        }
        public class LockB {public final static LockB lockb=new LockB();
        }

        执行结果:(99%的可能如下除了某一线程比较牛逼同时抢到了lockA和lockB)

                

http://www.lryc.cn/news/590713.html

相关文章:

  • [Mysql] Connector / C++ 使用
  • 二分查找算法(一)
  • 多目标优化|HKELM混合核极限学习机+NSGAII算法工艺参数优化、工程设计优化,四目标(最大化输出y1、最小化输出y2,y3,y4),Matlab完整源码
  • WP Force SSL Pro – HTTPS SSL Redirect Boost Your Website‘s Trust in Minutes!
  • 代码随想录算法训练营完结篇
  • 主流 TOP5 AI智能客服系统对比与推荐
  • Raydium CLMM 协议
  • Gradle vs Maven:构建工具世纪对决 —— 像乐高积木与标准模型之间的选择艺术
  • Transform的重要方法
  • excel分组展示业绩及增长率
  • 归一化与激活函数:深度学习的双引擎
  • 【WRFDA数据教程第一期】LITTLE_R 格式详细介绍
  • opencv 值类型 引用类型
  • 身份证号码姓名认证解决方案-身份证三要素API接口
  • Python+Selenium自动化
  • 【python】sys.executable、sys.argv、Path(__file__) 在PyInstaller打包前后的区别
  • Linux内核IPv4路由查找:LPC-Trie算法的深度实践
  • 门级网标仿真的时钟异常检查
  • 【C++高阶四】红黑树
  • ELK日志分析,涉及logstash、elasticsearch、kibana等多方面应用,必看!
  • 线程规则的制定者二:线程安全与冲入问题
  • 坚持继续布局32位MCU,进一步完善产品阵容,96Mhz主频CW32L012新品发布!
  • 选择亿林数据软件测试服务,为哈尔滨企业数字化转型赋能
  • 一叶障目不见森林
  • adb性能测试命令
  • 【知识图谱】Neo4j桌面版运行不起来怎么办?Neo4j Desktop无法打开!
  • Python18 —— 文件的写入
  • 大模型 认知能力 生物学启发
  • oracle会话控制和存储状态查询
  • Swift6.0基础知识 -- 可选2