初探多线程 ---(基本结构,案例展示)

    学习多线程的初衷是期望本学期能够开发出一个入门版的 Tank War 。作为初学者,我不得不从基础概念抓手,结合其基本框架,通过简单的案例展现来得到可观的进步和提升

  • 1.多线程的基本概念

    • 1.1 线程定义

      • 线程(Thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一个进程可以包含多个线程。线程有时也被称为轻量级进程。例如,当你运行一个软件时,这个软件本身就是一个进程,而进程中的多个任务,如数据读取、界面绘制等,可能由不同的线程来执行。

    • 1.2 进程,线程?

      • 进程是程序运行的实例,是系统进行资源分配和调度的基本单位。而线程是进程中的一个实体,它共享进程的资源,如内存地址空间、文件句柄等。线程的创建、切换和销毁比进程要轻量得多。在同一进程中,线程之间的通信比进程之间要简便,因为它们共享内存空间。

    • 1.3 线程的基本特征

      • 轻量级:线程的创建和销毁开销较小,切换速度也比进程快很多,因为它们不需要涉及用户态和内核态的切换。

      • 并发性:多线程可以在单个进程中实现并发执行,提高程序的响应速度和效率。在多核处理器系统中,可以真正实现并行运行。

  • 2. 多线程的基本结构

    • 2.1线程的生命周期

      • 对于多线程的生命周期可以简单地理解为上图
      • 2.1.1 新建(NEW)

        • 定义:线程对象被创建,但还未启动。

        • 特点

          • 此时线程已经分配了内存空间,但还没有分配CPU时间片。线程不会执行任何任务,直到调用start()方法。

        • 示例(以我的项目)
          • public class GameThread extends Thread{
            ......
            }
            
             public GameThread gt;
            
             //创建线程对象
             gt = new GameThread(g, balls);
            
            
      • 2.1.2 就绪 (Stand By)

        • 定义:线程已经准备好运行,等待操作系统分配CPU时间片

        • 特点

          • 线程在就绪队列中等待调度。

          • 操作系统会根据调度算法选择线程执行。

        • 示例:
          •  //启动线程,每个线程只能启动一次
             gt.start();
            
      • 2.1.3 运行(Running)

        • 定义:线程正在执行任务。
        • 特点

          • 此时线程获得了CPU时间片,执行线程体中的代码。

          • 线程可能会因为运行时间片用完、调用yield()方法或者发生I/O操作等原因进入阻塞状态。

        • 使用:
          • run() 方法的作用

            • 在多线程编程中,run() 方法是线程执行的核心逻辑。当线程被启动(通过 start() 方法)时,JVM 会调用线程的 run() 方法,从这里开始执行线程的逻辑任务。

            • 线程中的主操作代码都应写在 run() 方法中,你可以在这里实现线程需要完成的所有任务。

          • 线程的启动与执行

            • 当你调用 thread.start() 时,JVM 会为线程分配资源,并将其置于就绪状态(Runnable)。一旦线程获得 CPU 时间片,它就会调用 run() 方法,开始执行线程的任务。

            • run() 方法中的代码是线程的主体部分,它会一直执行,直到方法执行完毕或者被明确终止。

          • 代码示例

            • public void run() {
                      System.out.println(Thread.currentThread().getName() + ": 线程运行中...");
                      while (true) {
                          //延时
                          try {
                              Thread.sleep(10);
                          } catch (InterruptedException ex) {
                              throw new RuntimeException(ex);
                          }
              
                          for (int i = 0; i < balls.size(); i++) {
                              Ball ball = balls.get(i);
                              ball.drawBall(g);
                          }
              
                      }
                  }
      • 2.1.4 阻塞(Blocked)

        • 定义:线程暂时停止执行,等待某种条件满足。

        • 原因

          • 等待锁资源:线程尝试获取一个被其他线程持有的锁。

          • I/O操作:线程等待I/O操作完成(如文件读取、网络请求)。

          • 线程模拟睡眠:调用sleep()方法导致线程阻塞一段时间。

        • 代码示例:
          • while (true) {
                        //延时
                        try {
                            Thread.sleep(15);
                        } catch (InterruptedException ex) {
                            throw new RuntimeException(ex);
                        }
      • 2.1.5 等待(Waiting)

        • 定义:线程处于等待状态,等待其他线程的通知。

        • 原因

          • 调用wait()方法进入等待状态,直到其他线程调用notify()notifyAll()唤醒。

          • 进入等待状态后,线程释放锁资源。

        • 触发方式
          • 主要通过以下几种方式进入等待状态:

            • 调用 Object.wait() 方法。

            • 调用 Thread.join() 方法。

            • 调用 LockCondition 相关的等待方法(如 Condition.await())。

        • 特点
          • 等待中的线程不会自动结束等待,必须等待其他线程的通知(如 Object.notify()Object.notifyAll())才会从等待状态退出。

          • 等待中的线程会释放之前持有的对象锁,以便其他线程可以获取该锁并执行相关操作。

          • 处于等待状态的线程不会消耗CPU资源,因为它不会执行任何任务。

        • 退出等待状态的方式
          • 通过 notify()notifyAll() 方法

            • 如果线程是通过调用 Object.wait() 进入的等待状态,那么其他线程需要通过调用同一个对象的 notify() 方法唤醒一个等待线程,或者调用 notifyAll() 唤醒所有等待线程。

          • 线程中断

            • 如果等待中的线程被中断(通过 Thread.interrupt() 方法),它会抛出 InterruptedException 并退出等待状态。因此,在编写代码时需要捕获这个异常并进行处理。

        • 代码示例:
          • public void run() {
                    System.out.println(Thread.currentThread().getName() + ": 线程运行中...");
                    while (true) {
                        synchronized (lock) {     //使用 synchronized 块确保线程安全。
                            while (!running) {
                                try {
                                    lock.wait();//等待唤醒
                                } catch (InterruptedException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                            for (int i = 0; i < balls.size(); i++) {
                                Ball ball = balls.get(i);
                                ball.drawBall(g);
                            }
                        }
                        //延时
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException ex) {
                            throw new RuntimeException(ex);
                        }
            
                    }
                }
            
                public void pause() {//暂停线程
                    synchronized (lock) {
                        running = false;
                    }
                }
            
                public void resumeThread() {//唤醒线程
                    synchronized (lock) {
                        running = true;
                        lock.notify();//唤醒线程
                    }
                }
          • 
                @Override
                public void mouseClicked(MouseEvent e) {
                    int x = e.getX();
                    int y = e.getY();
                    //判断鼠标左右建
                    int key = e.getButton();
                    if (key == 1) {  //左键
                        ball = new Ball(x, y, 50);
                        balls.add(ball);
                    } else if (key == 3) {//右键
                        gt.pause();
                    } else if (key == 2) {//滚轮
                        gt.resumeThread();
                    }
                }
      • 2.1.6 超时等待状态(TimedWaiting)

        • 定义:超时等待状态是指线程在等待某个条件时设置了超时时间。如果在指定的时间内没有满足条件,线程会自动退出等待状态。

        • 触发方式:
          • 主要通过以下几种方式进入超时等待状态:

            • 调用 Object.wait(long timeout) 方法。

            • 调用 Thread.sleep(long millis) 方法。

            • 调用 LockCondition 相关的超时等待方法(如 Condition.await(long timeout, TimeUnit unit))。

        • 特点:
          • 与普通等待状态相比,超时等待状态具有时间限制,线程不会无限期地等待。

          • 超时等待中的线程同样会释放之前持有的对象锁(如果是通过 Object.waitCondition.await 进入的话)。

          • 如果在超时时间到达之前满足了条件(如被其他线程唤醒),线程会提前退出超时等待状态。

        • 退出超时等待
          • 条件满足

            • 如果其他线程在超时时间到达之前调用了唤醒方法(如 notify()notifyAll()),则线程会提前退出超时等待状态。

          • 超时时间到达

            • 如果在设置的超时时间内没有满足条件,线程会自动退出超时等待状态。

          • 线程中断

            • 如果等待中的线程被中断,它会抛出 InterruptedException 并退出超时等待状态。

      • 2.1.7 死亡(Dead)

        • 定义

          • 死亡状态是指线程已经完成了它的任务,或者由于某些原因(如异常、强制终止等)而停止运行。

          • 一旦线程进入死亡状态,它就无法再次启动,线程对象的生命周期结束。

        • 原因
          • 1. 正常完成 “run()” 任务
          • 2. 因异常终止
          • 3. 线程死亡的终点
            • 不可恢复性:一旦线程进入死亡状态,它就无法再次启动。如果需要重新执行任务,必须创建一个新的线程对象。

            • 资源释放:线程死亡后,系统会回收线程占用的资源(如栈空间、线程本地存储等)。

            • 线程对象仍然存在:虽然线程已经死亡,但线程对象仍然存在于内存中,直到被垃圾回收器回收。

        • 注意
          • 避免使用 Thread.stop()

            • Thread.stop() 方法会强制终止线程,可能导致资源泄漏或数据不一致。例如,如果线程持有某个对象的锁,调用 stop() 方法后,锁不会被释放,可能导致其他线程永远无法获取该锁。

            • 推荐使用标志位(如 volatile boolean running)来优雅地终止线程。

          • 处理未捕获的异常

            • 如果线程因未捕获的异常而终止,可能会导致程序的不稳定。可以通过 Thread.setUncaughtExceptionHandler() 方法为线程设置异常处理器,捕获并处理未捕获的异常。

          • 线程死亡后的资源清理

            • 在线程死亡之前,确保释放所有占用的资源,如关闭文件、释放锁等。

            • 可以在 run() 方法的最后或通过 finally 块进行资源清理。

        • 总结
          • 线程死亡是线程生命周期的终点,线程完成任务或因异常、强制终止而进入死亡状态。

          • 线程死亡后无法再次启动,如果需要重复执行任务,必须创建新的线程。

          • 在编程中,应尽量避免使用 Thread.stop() 方法,推荐使用标志位来优雅地终止线程。

          • 处理未捕获的异常和清理资源是线程编程中的重要注意事项,确保程序的稳定性和可靠性。

    • 2.2 线程的同步与互斥

  • s
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值