JAVA中线程小谈

本文介绍了Java中Object类的三个需在同步块调用的方法,阐述了线程的睡眠和唤醒机制,说明了线程关闭的条件、run方法和start方法的区别。还介绍了线程池的概念,包括ThreadPoolExecutor类及相关参数,以及线程池的处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先要介绍的是Object类的三个方法

wait(long timeout)

notify()

notifyAll()

这三个方法必须要在同步块中才能调用,即首先获取对象的monitor,常用的方式如下
 

 synchronize(obj){

       obj.wait()

        ...

}

当对象调用wait方法的时候,就意味着放弃了该资源(monitor),并进入了等待队列。

注:记得我们之前公司的产品,当一个线程需要睡眠的时候,就是用wait(long timeout)来实现的。我们的线程架构一般是   

 public void run(){

         while(!shutdown){

                 ...

               do something

         //需要sleep的时候

       synchronize(obj){

          obj.wait(time)

          ...
        }
   }
}

这样就可以通过shutdown这个变量来控制该线程的关闭,同时,如果系统在关闭的时候,该线程处于sleep状态,我们只需要调用notify()方法就可以将该线程从睡眠状态叫醒,然后关闭。如果这里用sleep()方法的话,就需要用interupt()方法。

   

protect boolean snooze(long sleeptime){
    synchronized(obj){
        if(wasAwoken){
            wasAwoken=false;
            return true;
        }

  try{
        if(sleeptime > 0){
            obj.wait(sleeptime);

         if(wasAwoken){
                wasAwoken=false;
                return true;
        }
    
       return false;
}catch(InterruptedException ex){
     log.info("Thread was interrupted-this should not happen,use wake() instead");
     return true;
}
         


protect boolean  wake(){

  synchronized(obj){
     wasAwoken=true;
     obj.notify();
  }

} 

这样就可以实现线程的睡眠和唤醒了。

 

一般的大工程都会有很多deamon thread ,  具体什么是deamon thread可以去查询,但是如果一个application 没有用户进程的话,JVM会自动关闭该application, 一般deamon thread 都是一个一直在run的线程。

1. 线程怎么样就关闭了??

   如果一个线程的run方法执行完毕,则这个线程就关闭了,所以一般的deamon thread 的run方法都是一个循环。

像前面介绍的可以利用shutdown的变量来控制循环是否结束。从而使线程退出。

2. 线程的run方法和start方法

   如果你直接调用一个线程的run方法,实际上还是在本线程执行了一个函数而已,并没有开启一个线程。只有当调用线程的start方法时候,才会在另一个线程中执行run方法。

3. 线程池的概念

   ThreadPoolExecutor该类是线程池的主类,像常见的几种线程池都是通过工厂类来调用ThreadPoolExecutor() ,只是具体的参数不同。

参考以下的链接:https://blog.youkuaiyun.com/xiaojin21cen/article/details/87359148

public  ThreadPoolExecutor(initcorePoolSize,
        int maximumPoolSize,
        long keepAliveTime,
        TimeUnit unit,
        BlockingQueue<Runnable> workQueue,
        ThreadFactory threadFactory,
        RejectedExecutionHandler handler

   initcorePoolSize:是指在空闲的时候,线程池中保留的线程池数

  maximumPoolSize:是指线程池中最大的线程数

   keepAliveTime:线池空闲的时间,当大于这个时间,空闲的线程会被销毁,达到initcorePoolSize的数值

   TimeUnit:是指时间的单位

   BlockingQueue:是指当线程达到initcorePoolSize的时候,进来的任务都需要提交到队列中,就是该队列。

   另外两个参数具体不介绍了。

 线程池的处理流程。

   比如initcorePoolSize是10,maximumPoolSize是20,BlockingQueue是ArrayBlockingQueue(10-有界的队列)

     1.当一个任务提交到线程池的时候,如果当前线程池中的线程小于initcorePoolSize -10,则立即会创建新的线程来执行该任务。

     2.当一个任务提交到线程池的时候,当前线程池中的线程已经大于initcorePoolSize-10,则会将任务放到ArrayBlockingQueue中,等待空闲线程来执行。

  3. 当一个任务提交到线程池的时候,当前线程池的线程已经大于initcorePoolSize-10,且ArrayBlockingQueue也满了,但是线程池中的线程数小于maximumPoolSize-20,则会创建线程来执行任务。

4.当一个任务提交到线程池的时候,线程数大于maximumPoolSize ,则会使用拒绝策略来处理。

 

不同的线程池,主要是initcorePoolSize,maximumPoolSize,以及BlockingQueue的差别,具体可以参考

https://www.jianshu.com/p/78424e42d4ea

 

<think>首先,用户的问题是:“我想了解Java中线程的相关知识或使用方法请问Java线程使用指南”,基于系统级指令,我需要使用中文回答,并确保LaTeX语法正确。系统指令关键点:-行内数学表达式用$...$。-独立公式用$$...$$并单独成段。-生成相关问题(用§§相关问题§§)。-在回答中自然地添加引用标识,如[^1]。-结构清晰,帮助用户逐步解决问题。-用户要求参考站内引用,但我作为AI,实际没有真实引用来源,我需要基于提供的引用内容创建回答,同时参考上一次的问题和回答,但这次是第一次交互。提供的引用:-[^1]:关于匿名内部类的方式实现线程和线程耦合性问题。-[^2]:守护线程和用户线程的区别,以及线程的生命周期。-[^3]:线程池使用指南,包括创建线程的资源消耗原因。用户上次的问题没有显示,因为我假设这是第一次回答,但系统说“参考用户上一次的问题和你的回答”,但上下文没有显示任何上一次问题,所以我应该基于当前内容。我需要创建一个关于Java线程的使用指南的结构化回答。结构清晰,帮助用户逐步理解。大纲回答内容:1.**介绍线程的基本概念**:Java中线程是什么。2.**创建线程的方法**:包括继承Thread类、实现Runnable接口、匿名内部类(引用[^1]中提到)。3.**线程的类型**:守护线程和用户线程(引用[^2])。4.**线程的生命周期**:简要介绍(从引用[^2])。5.**线程池的使用**:为什么使用线程池(从引用[^3]),以及如何用Executor框架实现。6.**实践例子**:提供代码示例。7.**最佳实践和注意事项**:例如避免耦合(引用[^1]),资源管理(引用[^3])。8.**常见问题**:生成相关问题。回答结构:-开头:简要介绍Java线程。-主体:分点介绍关键知识点,带有示例代码。-结尾:总结并列出相关问题。生成相关问题:在最后用§§相关问题§§列出几个相关问题,如:1.如何避免线程死锁?2.线程池的参数配置是怎样的?3.Java并发库中的其他工具是什么?在回答中自然地添加引用标识:当引用提供的内容时,添加[^1],[^2],[^3]。例如:-到匿名内部类,加[^1]。-到守护线程,加[^2]。-到线程池原因,加[^3]。LaTeX:因为线程是编程主题,可能不需要太多数学表达式,但如果有数学相关内容,应用指令。这里可能少,但为了遵守,如果有变量使用$...$。使用中文:整个回答用中文。最后,确保代码块正确显示,如示例中的Python代码。用户提供了Java代码引用,所以我应该在回答中包含Java代码示例。具体步骤:1.**介绍Java线程**:线程是程序执行的最小单位,Java中通过Thread类和Runnable接口实现并发。2.**创建线程的方法**:-继承Thread类并重写run方法(但耦合性强,参考[^1])。-实现Runnable接口(更好,解耦)。-匿名内部类方式:如引用[^1]中的例子。-代码示例。3.**守护线程vs.用户线程**:守护线程在JVM中没有用户线程时退出,参考[^2]。4.**线程的生命周期**:新建、就绪、运行、阻塞、死亡。5.**线程池**:为什么使用线程池?减少资源消耗、减少上下文切换,参考[^3]。使用ExecutorService。6.**实例代码**:展示如何使用线程池。7.**最佳实践**:使用Runnable接口减少耦合;合理使用线程池。回答问题Java线程使用指南”,所以侧重于使用方法。引用自然添加:例如,当说“匿名内部类方式继承Thread类开启多线程”时,末尾加[^1]。列出相关问题。</think> ### Java线程使用指南 Java线程是实现程序并发执行的基本单位,广泛应用于多任务处理、网络通信等场景。本指南将帮助你逐步了解Java线程的使用方法,包括核心概念、创建方式、类型区分、生命周期管理、线程池应用等关键知识。内容基于可靠实践和相关引用,确保信息真实可靠。 --- #### 1. **线程的基本概念** Java中,线程是独立执行路径,每个线程都有自己的方法调用栈。线程允许程序同时执行多个任务,提高性能。[^1] 例如,线程可以并行处理文件读写或网络请求。 #### 2. **创建线程的方法** Java支持多种方式创建线程,推荐使用`Runnable`接口以降低耦合性(避免线程和任务紧密绑定)。[^1] - **方式一:继承Thread类** - 缺点:任务内容与线程耦合,只能执行特定`run()`方法中的任务。[^1] - 示例代码: ```java public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("线程执行:" + i); } } public static void main(String[] args) { MyThread t = new MyThread(); t.start(); // 启动线程 } } ``` - **方式二:实现Runnable接口** - 优点:任务与线程分离,同一个任务可被多个线程复用。 - 示例代码: ```java public class MyRunnable implements Runnable { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("任务执行:" + i); } } public static void main(String[] args) { Runnable task = new MyRunnable(); Thread t = new Thread(task); // 将任务绑定到新线程 t.start(); } } ``` - **方式三:匿名内部类简化版** - 适用于简单任务,直接在代码中定义线程。 - 示例代码(参考引用[^1]): ```java public class UseNoNameClass { public static void main(String[] args) { Thread t = new Thread() { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("匿名线程:" + i); } } }; t.start(); // 更简洁方式 new Thread() { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("直接启动线程:" + i); } } }.start(); } } ``` - 注意:匿名内部类易读性好,但耦合性问题依然存在,建议优先用`Runnable`。[^1] #### 3. **线程的类型:守护线程与用户线程** Java线程分为两类: - **用户线程**:默认为用户线程,JVM会等待所有用户线程结束才退出。如主线程。 - **守护线程**:用于服务用户线程(如垃圾回收),当所有用户线程结束时,JVM自动终止守护线程(“兔死狗烹”效应)。[^2] 设置守护线程的方法: - 在`start()`前调用`thread.setDaemon(true)`。 - 示例代码: ```java public class DaemonThreadExample { public static void main(String[] args) { Thread daemonThread = new Thread(() -> { while (true) { System.out.println("守护线程运行中..."); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); daemonThread.setDaemon(true); // 设置为守护线程 daemonThread.start(); // 用户线程(主线程) for (int i = 0; i < 5; i++) { System.out.println("用户线程执行: " + i); } } } ``` - 当主线程结束,守护线程自动退出。[^2] #### 4. **线程的生命周期** 线程状态包括:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)、等待(Waiting)、终止(Terminated)。[^2] - **关键方法**: - `start()`:启动线程,进入就绪状态。 - `sleep(long millis)`:暂停线程指定毫秒。 - `join()`:等待线程终止。 - `yield()`:让出CPU资源。 - 状态图: - 新建 → `start()` → 就绪 → (系统调度) → 运行 → `sleep()/wait()` → 阻塞 → 唤醒 → 就绪 → 终止 #### 5. **线程池的使用** 线程池减少频繁创建和销毁线程的开销,避免资源浪费(每个线程消耗约1MB栈内存,可通过`-Xss`调整)。[^3] 使用`java.util.concurrent.ExecutorService`。 - **为什么用线程池**? - 减少内存消耗和CPU上下文切换(引用[^3]指出,过多线程会降低性能)。 - 当任务处理时间远小于线程创建时间时,线程池能提高效率。 - **线程池示例(使用Executor框架)**: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { public static void main(String[] args) { ExecutorService executor = Executors.newFixedThreadPool(4); // 创建固定大小线程池 for (int i = 0; i < 10; i++) { executor.submit(() -> { System.out.println("任务执行: " + Thread.currentThread().getName()); // 执行任务逻辑 }); } executor.shutdown(); // 关闭线程池 } } ``` - **参数说明**: - `newFixedThreadPool(int n)`: 创建固定数量的线程池。 - 替代方案:`newCachedThreadPool()` (自动伸缩), `newScheduledThreadPool()` (定时任务)。 - 管理线程池可避免内存溢出和性能下降。[^3] #### 6. **最佳实践与注意事项** - **避免耦合**:多用`Runnable`接口而非继承Thread类,实现任务可复用性(如线程1执行任务A,线程2执行任务B)参考[^1]。 - **资源管理**:在高并发场景使用线程池控制线程数量(引用[^3]建议根据系统资源设置最大线程数)。 - **线程安全**:使用`synchronized`或`Lock`防止竞态条件。 - **性能优化**:监控线程CPU使用率,避免阻塞过多。 - **调试技巧**:利用IDE工具(如IntelliJ)分析线程状态。 --- #### 总结 Java线程是实现并发的核心工具,理解其创建方式(如Runnable接口)、类型区分(守护线程)和线程池管理(Executor框架)至关重要。通过合理使用线程池和避免耦合,能提升应用性能和可靠性。[^1][^2][^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值