多线程Java程序中常见错误的巧处理

[RK3588从入门到精通] 专栏目录及介绍 阅读详情
多线程Java程序中常见错误的巧处理

作者:俞良松    出处:编程爱好者        

在几乎所有编程语言中,由于多线程引发的错误都有着难以再现的特点,程序的死锁或其它多线程错误可能只在某些特殊的情形下才出现,或在不同的VM上运行同一个程序时错误表现不同。因此,在编写多线程程序时,事先认识和防范可能出现的错误特别重要。

  无论是客户端还是服务器端多线程Java程序,最常见的多线程问题包括死锁、隐性死锁和数据竞争。

  死锁

  死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

  导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性的访问权。当线程访问对象时,线程会给对象加锁,而这个锁导致其它也想访问同一对象的线程被阻塞,直至第一个线程释放它加在对象上的锁。

  由于这个原因,在使用“synchronized”关键词时,很容易出现两个线程互相等待对方做出某个动作的情形。代码一是一个导致死锁的简单例子。

//代码一
class Deadlocker {
int field_1;
private Object lock_1 = new int[1];
int field_2;
private Object lock_2 = new int[1];

public void method1(int value) {
  “synchronized” (lock_1) {
   “synchronized” (lock_2) {
    field_1 = 0; field_2 = 0;
   }
  }
}

public void method2(int value) {
  “synchronized” (lock_2) {
   “synchronized” (lock_1) {
    field_1 = 0; field_2 = 0;
   }
  }
}
}



  参考代码一,考虑下面的过程:

  ◆ 一个线程(ThreadA)调用method1()。

  ◆ ThreadA在lock_1上同步,但允许被抢先执行。

  ◆ 另一个线程(ThreadB)开始执行。

  ◆ ThreadB调用method2()。

  ◆ ThreadB获得lock_2,继续执行,企图获得lock_1。但ThreadB不能获得lock_1,因为ThreadA占有lock_1。

  ◆ 现在,ThreadB阻塞,因为它在等待ThreadA释放lock_1。

  ◆ 现在轮到ThreadA继续执行。ThreadA试图获得lock_2,但不能成功,因为lock_2已经被ThreadB占有了。

  ◆ ThreadA和ThreadB都被阻塞,程序死锁。

  当然,大多数的死锁不会这么显而易见,需要仔细分析代码才能看出,对于规模较大的多线程程序来说尤其如此。好的线程分析工具,例如JProbe Threadalyzer能够分析死锁并指出产生问题的代码位置。

  隐性死锁

  隐性死锁由于不规范的编程方式引起,但不一定每次测试运行时都会出现程序死锁的情形。由于这个原因,一些隐性死锁可能要到应用正式发布之后才会被发现,因此它的危害性比普通死锁更大。下面介绍两种导致隐性死锁的情况:加锁次序和占有并等待。

  加锁次序

  当多个并发的线程分别试图同时占有两个锁时,会出现加锁次序冲突的情形。如果一个线程占有了另一个线程必需的锁,就有可能出现死锁。考虑下面的情形,ThreadA和ThreadB两个线程分别需要同时拥有lock_1、lock_2两个锁,加锁过程可能如下:

  ◆ ThreadA获得lock_1;

  ◆ ThreadA被抢占,VM调度程序转到ThreadB;

  ◆ ThreadB获得lock_2;

  ◆ ThreadB被抢占,VM调度程序转到ThreadA;

  ◆ ThreadA试图获得lock_2,但lock_2被ThreadB占有,所以ThreadA阻塞;

  ◆ 调度程序转到ThreadB;

  ◆ ThreadB试图获得lock_1,但lock_1被ThreadA占有,所以ThreadB阻塞;

  ◆ ThreadA和ThreadB死锁。

  必须指出的是,在代码丝毫不做变动的情况下,有些时候上述死锁过程不会出现,VM调度程序可能让其中一个线程同时获得lock_1和lock_2两个锁,即线程获取两个锁的过程没有被中断。在这种情形下,常规的死锁检测很难确定错误所在。

  占有并等待

  如果一个线程获得了一个锁之后还要等待来自另一个线程的通知,可能出现另一种隐性死锁,考虑代码二。

//代码二
public class queue {
static java.lang.Object queueLock_;
Producer producer_;
Consumer consumer_;

public class Producer {
  void produce() {
   while (!done) {
    “synchronized” (queueLock_) {
     produceItemAndAddItToQueue();
     “synchronized” (consumer_) {
      consumer_.notify();
     }
    }
   }
  }

  public class Consumer {
   consume() {
    while (!done) {
     “synchronized” (queueLock_) {
      “synchronized” (consumer_) {
       consumer_.wait();
      }
      removeItemFromQueueAndProcessIt();
     }
    }
   }
  }
}
}



  在代码二中,Producer向队列加入一项新的内容后通知Consumer,以便它处理新的内容。问题在于,Consumer可能保持加在队列上的锁,阻止Producer访问队列,甚至在Consumer等待Producer的通知时也会继续保持锁。这样,由于Producer不能向队列添加新的内容,而Consumer却在等待Producer加入新内容的通知,结果就导致了死锁。

  在等待时占有的锁是一种隐性的死锁,这是因为事情可能按照比较理想的情况发展—Producer线程不需要被Consumer占据的锁。尽管如此,除非有绝对可靠的理由肯定Producer线程永远不需要该锁,否则这种编程方式仍是不安全的。有时“占有并等待”还可能引发一连串的线程等待,例如,线程A占有线程B需要的锁并等待,而线程B又占有线程C需要的锁并等待等。

  要改正代码二的错误,只需修改Consumer类,把wait()移出“synchronized”()即可。

  数据竞争

  数据竞争是由于访问共享资源(例如变量)时缺乏或不适当地运用同步机制引起。如果没有正确地限定某一时刻某一个线程可以访问变量,就会出现数据竞争,此时赢得竞争的线程获得访问许可,但会导致不可预知的结果。

  由于线程的运行可以在任何时候被中断(即运行机会被其它线程抢占),所以不能假定先开始运行的线程总是比后开始运行的线程先访问到两者共享的数据。另外,在不同的VM上,线程的调度方式也可能不同,从而使数据竞争问题更加复杂。

  有时,数据竞争不会影响程序的最终运行结果,但在另一些时候,有可能导致不可预料的结果。

  良性数据竞争

  并非所有的数据竞争都是错误。考虑代码三的例子。假设getHouse()向所有的线程返回同一House,可以看出,这里会出现竞争:BrickLayer从House.foundationReady_读取,而FoundationPourer写入到House.foundationReady_。

//代码三
public class House {
public volatile boolean foundationReady_ = false;
}

public class FoundationPourer extends Thread {
public void run() {
  House a = getHouse();
  a.foundationReady_ = true;
}
}

public class BrickLayer extends Thread {
public void run() {
  House a = getHouse();
   while (!a.foundationReady_) {
    try {
     Thread.sleep(500);
    }
    catch (Exception e) {
     System.err.println(“Exception:” + e);
    }
   }
  }
}
}



  尽管存在竞争,但根据Java VM规范,Boolean数据的读取和写入都是原则性的,也就是说,VM不能中断线程的读取或写入操作。一旦数据改动成功,不存在将它改回原来数据的必要(不需要“回退”),所以代码三的数据竞争是良性竞争,代码是安全的。

  恶性数据竞争

  首先看一下代码四的例子。

//代码四
public class Account {
private int balance_; // 账户余额
public int getBalance(void) {
  return balance_;
}
public void setBalance(int setting) {
  balance_ = setting;
}
}

public class CustomerInfo {
private int numAccounts_;
private Account[] accounts_;
public void withdraw(int accountNumber, int amount) {
  int temp = accounts_[accountNumber].getBalance();
  temp = temp - amount;
  accounts_[accountNumber].setBalance(temp);
}
public void deposit(int accountNumber, int amount) {
  int temp = accounts_[accountNumber].getBalance();
  temp = temp + amount;
  accounts_[accountNumber].setBalance(temp);
}
}



  如果丈夫A和妻子B试图通过不同的银行柜员机同时向同一账户存钱,会发生什么事情?让我们假设账户的初始余额是100元,看看程序的一种可能的执行经过。

  B存钱25元,她的柜员机开始执行deposit()。首先取得当前余额100,把这个余额保存在本地的临时变量,然后把临时变量加25,临时变量的值变成125。现在,在调用setBalance()之前,线程调度器中断了该线程。

  A存入50元。当B的线程仍处于挂起状态时,A这面开始执行deposit():getBalance()返回100(因为这时B的线程尚未把修改后的余额写入),A的线程在现有余额的基础上加50得到150,并把150这个值保存到临时变量。接着,A的线程在调用setBalance()之前,也被中断执行。

  现在,B的线程接着运行,把保存在临时变量中的值(125)写入到余额,柜员机告诉B说交易完成,账户余额是125元。接下来,A的线程继续运行,把临时变量的值(150)写入到余额,柜员机告诉A说交易完成,账户余额是150元。

  最后得到的结果是什么?B的存款消失不见,就像B根本没有存过钱一样。

  也许有人会认为,可以把getBalance()和setBalance()改成同步方法保护Account.balance_,解决数据竞争问题。其实这种办法是行不通的。“synchronized”关键词可以确保同一时刻只有一个线程执行getBalance()或setBalance()方法,但这不能在一个线程操作期间阻止另一个线程修改账户余额。

要正确运用“synchronized”关键词,就必须认识到这里要保护的是整个交易过程不被另一个线程干扰,而不仅仅是对数据访问的某一个步骤进行保护。

  所以,本例的关键是当一个线程获得当前余额之后,要保证其它的线程不能修改余额,直到第一个线程的余额处理工作全部完成。正确的修改方法是把deposit()和withdraw()改成同步方法。

  死锁、隐性死锁和数据竞争是Java多线程编程中最常见的错误。要写出健壮的多线程代码,正确理解和运用“synchronized”关键词是很重要的。另外,好的线程分析工具,例如JProbe Threadalyzer,能够极大地简化错误检测。对于分析那些不一定每次执行时都会出现的错误,分析工具尤其有用。

【CUDA Runtime】第一个“Hello World“程序 CUDA(Compute Unified Device Architecture)是 NVIDIA 提供的一个并行计算平台和编程模型,旨在通过利用 GPU 的强大计算能力来加速计算密集型应用。对于许多开发者而言,开始学习 CUDA 编程的第一步通常是编写一个简单的“Hello World”程序。尽管 CUDA 的实际应用远远超出一个简单的例子,但这个初步的示例能帮助开发者快速入门,理解基本的 CUDA 编程流程。 阅读详情

相关推荐

WGS-84 ECEF ENU 坐标系学习记录

地球坐标系固定在地球上而随地球一起在空间做公转和自转运动,因此地球上任一固定点在地球坐标系的坐标就不会由于地球旋转而变化。地心地固直角坐标系和大地坐标系都属于这种坐标系。 一、 几种坐标系 地心地固直角坐标系(ECEF)坐标系 其原点为地球的质心,x轴延伸通过本初子午线(0度经度)和赤道(0deglatitude)的交点。 z轴延伸通过的北极(即,与地球旋转轴重合)。 y轴完成右手坐标系,穿...

YYshuangshuang的博客 1万+

常见线程池报错异常记录:

1.java.util.concurrent.RejectedExecutionException 同事今天服务报错如下,看了下线程池的创建方式:方法逻辑是创建临时线程池然后关闭,他在代码中获取的全局线程池,方法结束给关闭了,这个异常相对比较简单,自定义线程池的时候,新建的线程加入不到线程池中,触发拒绝策略。 java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@72cffe5e re

l1994m的博客 1216

避开这些坑!STM32H750 IAP升级实战:从Bootloader设计到APP接收的完整避坑指南

本文深入探讨STM32H750 IAP升级的完整避坑指南,从Bootloader设计到APP接收的全流程实践。特别针对QSPI Flash运行限制、SRAM跳转机制和固件校验等核心问题,提供工业级解决方案,帮助开发者提升升级成功率和系统稳定性。

weixin_30471561的博客 275

Java新手学习入门总结:多线程失误知识点梳理

很多Java新手在刚接触线程时都会被其复杂的知识点搞晕,在实际应用中同样错误不断。如何才能快速掌握多线程呢?常见的Java多线程错误有哪些?接下来千锋广州Java培训小编就给大家分享Java新手学习入门中多线程失误梳理。 无论是客户端还是服务器端多线程Java程序,最常见的多线程问题包括死锁、隐性死锁和数据竞争。 1、死锁 多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。 导致死锁的根源在于不适当地运用“synchronized”关键词来管理

weixin_43703144的博客 228

金蝶ERP元数据解析:字段属性与表结构映射实战

本文深入解析金蝶ERP元数据的核心价值与应用实践,详细介绍了字段属性与表结构映射的关键技术。通过缓存获取与工具类直连两种方案对比,结合实际案例展示元数据在数据库校验、动态表单生成等场景的应用,帮助开发者高效解决二次开发中的常见问题。

weixin_33738555的博客 433

黏菌优化算法(SMA)详解:从生物智能到优化引擎

黏菌优化算法(SMA)是一种新型生物启发式优化算法,模拟多头绒泡菌的智能觅食行为。该算法包含接近食物、包围食物和抓取食物三个阶段,通过数学模型将生物行为转化为优化过程。SMA采用权重自适应机制和参数振荡模型实现探索与开发的平衡,具有参数简单、收敛性好等优势。算法可通过MATLAB/Python实现,并应用于函数优化、工程设计和神经网络训练等领域。研究表明SMA在求解复杂优化问题时表现优异,未来可通过混沌增强、混合策略等方式进一步提升性能。

qq_42212808的博客 659

ThreadLocal原理和 java类全局静态变量在多线程中数据混乱问题

引自:ThreadLocal原理和 java类全局静态变量在多线程中数据混乱问题 - X凯 - 博客园 一般想要在不同方法中传递上下文数据,会使用全局变量,而想在不同类中传递上下文数据,则会使用全局静态变量,因为静态全局变量是属于类一直存在的,但是在多线程操作状态下,java类全局静态变量在多线程中会出现数据混乱问题,因为多个线程同时对同一个静态变量操作时,无法确保每个线程取出的值是自己放的值。 这时就出现了ThreadLocal: ThreadLocal众所周知,可以把变量绑定到到某一线程上,这个th.

qq_35648576的博客 1667

RK3399平台入门到精通系列讲解 - 总目录

欢迎大家来到内核笔记的《RK3399平台开发入门到精通系列讲解》,开始前博主先列出RK3399平台学习的大纲,同时这也可以作为大家学习RK3399内核与安卓框架的参考。下面蓝字都是传送门,点击进入即可:...

内核笔记 10万+

Java多线程中static变量总结

参考文章:Java多线程中static变量的使用 (一)静态变量:线程非安全 1、静态变量:使用static关键字定义的变量。static可以修饰变量和方法,也有static静态代码块。被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。 用public修饰的static成员变量...

justlpf的专栏 3045

SAP Group Reporting入门指南(8)--业务操作

关于资产负债表类的匹配,系统预设了SGR01和SGR02两个方法,主要区别在于数据的来源范围,SGR01取自ACDOCA和ACDOCU,如果是0F类型的就会直接显示日记账分录(期间是000的除外,这类凭证是年结结转的,系统不再跨年追溯),如果是其他类型就显示合并分录。这里是我觉得SAP还需要优化的地方。基于ICMR的抵消任务中通常将抵销差异细分为交易差异、货币换算差异和其他差异,这在基于重分类抵消任务是无法实现的,基于重分类抵消只知道一个差异总额,很难从合并抵消凭证看出差异的来源,也没有审计线索可言。

bflyfish的博客 275

即插即用篇 | 手把手教你 YOLOv11 添加注意力机制 | 适用于【检测任务】【分类任务】【分割任务】【关键点任务】| 20+ 种全打通!

即插即用篇 | 手把手教你 YOLOv11 添加注意力机制 | 适用于【检测任务】【分类任务】【分割任务】【关键点任务】| 20+ 种全打通!

Coding... 2831

线程中出现异常的处理

在紧密交织的多线程环境中,异常处理是一个经常被讨论的容易被忽视的关键部分。这并不奇怪,因为在编写并发代码时,管理和理解可能出现的各种异常条件可能是一个挑战。在单线程环境中,发生异常时,异常信息会立刻被捕获并处理,然而,在多线程环境中的异常处理复杂性要高很多。未被正确处理的异常可能导致全局性影响,甚至系统崩溃。这给程序稳定性带来威胁,且可能会导致无法预料的行为。因此,对于编写健壮且可靠的并发代码来说,理解并且正确处理线程中的异常是至关重要的。本文旨在深入探讨Java中线程级别的异常处理。

m0_62943934的博客 2591

【数据集】1980-2020年(5年)土地利用分类数据-中国科学院

简介1980-2020年每五年土地利用数据-中国科学院

WW、forever的博客 3931

Java多线程中常见错误处理

<br />在几乎所有编程语言中,由于多线程引发的错误都有着难以再现的特点,程序的死锁或其它多线程错误可能只在某些特殊的情形下才出现,或在不同的VM上运行同一个程序时错误表现不同。因此,在编写多线程程序时,事先认识和防范可能出现的错误特别重要。 <br />  无论是客户端还是服务器端多线程Java程序,最常见的多线程问题包括死锁、隐性死锁和数据竞争。<br />死锁<br />  死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常

zichun921的专栏 5058

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

开发者必备:处理常见错误与问题的详细步骤

在软件开发过程中,开发者经常会遇到各种各样的错误和问题。无论是语法错误、运行时异常,还是配置问题,这些问题都会影响开发效率和程序的稳定性。尤其对于初学者来说,如何高效定位和解决这些问题是成为优秀开发者的关键。本篇文章将为大家提供一份详细的常见错误处理指南。从基础的语法错误到复杂的运行时异常,我将一步一步带你了解如何分析和解决问题。此外,我们还会深入探讨一些开发过程中可能遇到的配置错误和性能问题,并给出实际的解决方法。

沉淀所学,分享所思,让热爱与成长同行。商务记录AI实战经验,助力开发者快速成长。 热爱AI,希望做出有影响力的技术成果。 技术点亮生活,分享连接价值与机会。 专注AI落地实战,陪你走好技术每一步。 1万+

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Java多线程编程:避免常见陷阱

以下是针对Java多线程编程中常见陷阱的深度解析与解决方案,结合典型错误案例和最佳实践,帮助开发者编写安全可靠的多线程代码:错误示例: 解决方案: 2. 可见性问题 错误场景: 修复方案: 二、死锁与资源竞争 1. 死锁形成条件 规避方法: 2. 活锁(Livelock) 典型场景: 线程不断重试失败操作(如消息处理失败后反复回滚)解决方案: 三、线程管理陷阱 1. 线程池滥用 错误用法: 正确方案: 2. 线程泄漏 典型场景: 规避方法: 四、并发工具误用 1. 错误使用ConcurrentHas

免费 1936

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Java业务开发常见错误100例笔记

一、多线程相关 1、ThreadLocal ThreadLocal适用于变量在线程间隔离,而在方法或类间共享的场景 程序运行在Tomcat中,执行程序的线程是Tomcat的工作线程,而Tomcat的工作线程是基于线程池的。线程池会重用固定的几个线程,所以使用ThreadLocal来存放一些数据时,需要特别注意在代码运行完后,需要在代码的finally代码块中,显式清除ThreadLocal中的数据...

hxt的博客 1万+

java8多线程运行程序_线程,代码和数据–多线程Java程序实际运行的方式

java8多线程运行程序 有些事情是您在学术或培训班上没有学到的,经过几年的工作经验后才逐渐了解,然后才意识到,这是非常基本的事情,我为什么错过了这么多年。 了解多线程Java程序的执行方式就是其中之一。 您肯定已经听说过线程,如何启动线程,如何停止线程,诸如其独立的执行路径之类的定义,处理线程间通信的所有时髦库,但是在调试多线程Java程序时,您会斗争。 至少我可以从我的亲身经历中说出来。...

最佳 Java 编程 469

《Java 多线程:3 个常见错误让程序崩溃(附解决方案)》

Java 多线程编程能显著提升程序性能,但也容易引入隐蔽的错误,导致程序崩溃或行为异常。本文将分析三个最常见的错误——竞态条件、死锁和内存可见性问题,并提供实用的解决方案。每个错误都配有代码示例和修复建议,帮助您避免这些陷阱。竞态条件发生在多个线程同时访问和修改共享数据时,导致数据不一致或程序崩溃。例如,两个线程同时增加一个计数器,可能因非原子操作而丢失更新。错误示例代码: 原因分析: 操作不是原子的,涉及读取、修改和写入。多个线程同时执行时,可能发生覆盖,导致计数器值错误。解决方案: 使用 关键字

jsgdfgxjsgdjxc的博客 494

JAVA多线程常见的十大问题

JAVA多线程常见的十大问题讲解

学习记录和总结 1万+

Android常见异常处理

Android常见异常处理什么是异常?Exception是指在程序运行过程中所出现的不可预测的可处理可恢复的问题,这些错误会干扰到指令的正常执行,从而造成程序的异常退出或者意外终止。比如:FileNotFountException、NullPointException.Error是指程序运行过程中较为严重的不可挽回的问题,大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟

水寒 4215

程序员必知:Java代码常见的十种错误

每一个程序员在编写代码的过程中都免不了出现错误或是小的失误,这些小的错误和失误往往使得程序员还得返工。那么,如何才能尽量避免这些错误的发生呢?笔者总结只有在日常的编写代码中总结出经验,在这篇文章中,笔者列出了10个Java编程中常见的错误,你可以把这些错误添加到你的代码审查的检查

爱是一种信仰 1863

Java日期时间处理常见错误与bug及解决方案详解

Java日期时间处理常见问题及解决方案 摘要:Java日期时间处理中存在时区处理、日期计算、格式解析等常见问题。时区问题可通过显式指定时区和使用Java 8的ZonedDateTime解决;日期计算应优先使用Java 8的不可变API并正确处理边界情况;格式解析要注意线程安全问题和宽松解析模式。最佳实践包括:始终显式指定时区、使用Java 8日期API、区分不同时间单位、采用严格解析模式。新版java.time包提供了线程安全且设计良好的日期时间处理方案,应优先使用。

本博客聚焦 YOLOv11 全流程落地,涵盖架构优化、数据集处理、训练技巧与多场景部署,兼及国产数据库、Java 开发与 AI 模型应用,内容兼顾理论与工程实践,为开发者提供系统干货,助力高效提升技术能力。 1199

编写多线程Java应用程序

当编写使用AWT或Swing的图形程序时,除了最琐碎的程序之外,其他所有程序都需要多线程。 线程编程带来了许多困难,许多开发人员经常发现自己容易陷入诸如错误的应用程序行为和死锁的应用程序之类的问题。 在本文中,我们将探讨与多线程相关的问题,并讨论最常见陷阱的解决方案。 什么是线程? 一个程序或进程可以包含多个线程,这些线程根据程序代码执行指令。 就像可以在一台计算机上运行的多个进程...

cusi77914的博客 681
上一篇: 彻底明白Java的IO系统
下一篇: 实现Swing的JTables和Excel间的复制和粘贴功能
皮鲁
博客等级 码龄23年 44粉丝 73原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值