JAVA多线程并发(一)-- JAVA线程简介

一、JAVA线程实现/创建方式

在Java中,线程的创建与实现主要有三种方式:继承Thread类、实现Runnable接口以及使用Callable接口配合FutureTask和ExecutorService。下面将分别介绍这三种方式,并通过实际案例代码进行演示。

1. 继承Thread类

Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。

public class MyThread extends Thread {
    public void run() {
        System.out.println("MyThread.run()");
    }
}
MyThread myThread1 = new MyThread();
myThread1.start();

2. 实现Runnable接口

实现Runnable接口并重写其run()方法,然后将该对象作为参数传递给Thread类的构造方法,也可以创建并启动一个线程。这种方式更加灵活,因为Java不支持多继承,但可以实现多个接口。

public class MyRunnable implements Runnable {  
    @Override  
    public void run() {  
        for (int i = 0; i < 5; i++) {  
            System.out.println("MyRunnable is running: " + i);  
            try {  
                Thread.sleep(1000); // 模拟耗时操作  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
  
    public static void main(String[] args) {  
        Thread thread = new Thread(new MyRunnable());  
        thread.start(); // 启动线程  
  
        // 主线程代码与上面案例相同  
    }  
}

3. 使用Callable接口配合FutureTask和ExecutorService

这种方式可以实现有返回值的线程,并且可以处理异常。

  • Callable接口类似于Runnable接口,但call()方法可以有返回值并且可以抛出异常;
  • FutureTask是Callable接口的实现类,ExecutorService可以管理线程的启动、执行和关闭。
import java.util.concurrent.*;  
  
public class MyCallable implements Callable<Integer> {  
    @Override  
    public Integer call() throws Exception {  
        int sum = 0;  
        for (int i = 0; i <= 100; i++) {  
            sum += i;  
            Thread.sleep(10); // 模拟耗时操作  
        }  
        return sum;  
    }  
  
    public static void main(String[] args) throws ExecutionException, InterruptedException {  
        ExecutorService executorService = Executors.newSingleThreadExecutor();  
        Future<Integer> future = executorService.submit(new MyCallable());  
  
        // 主线程可以继续执行其他任务  
  
        // 当需要获取子线程的计算结果时  
        Integer result = future.get(); // get()方法会阻塞,直到子线程执行完毕并返回结果  
        System.out.println("Result from MyCallable: " + result);  
  
        executorService.shutdown(); // 关闭线程池  
    }  
}

以上三种方式各有优缺点,可以根据具体需求选择合适的方式来创建和管理线程。在实际应用中,通常会选择使用线程池(如ExecutorService)来管理线程,以提高系统的性能和稳定性。

二、JAVA 线程池 ExecutorService

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService

在这里插入图片描述

三、4种线程池

1. newFixedThreadPool:固定大小的线程池。

  • 特点:可以控制最大并发数,即线程池中的线程数量是固定的。
  • 使用场景:适用于需要控制并发数,且任务数量相对稳定的场景。
  • 示例:
ExecutorService executorService = Executors.newFixedThreadPool(60);

2. newCachedThreadPool:可缓存的线程池。

  • 特点:线程池大小不受限,可以根据需要动态地创建和销毁线程。如果线程池中的线程空闲时间超过指定时间,则会被销毁;如果线程池中的线程不足,则会创建新的线程。
  • 使用场景:适用于执行大量短时间异步任务的情况,可以减少因创建和销毁线程所带来的开销
  • 示例:
ExecutorService executorService = Executors.newCachedThreadPool();

3. newScheduledThreadPool:定时和周期执行任务的线程池。

  • 特点:支持定时及周期性任务执行。
  • 使用场景:适用于需要定时或周期性执行任务的场景,如定时发送邮件、定时检查数据库等。
  • 示例:
// 创建一个固定大小的ScheduledExecutorService线程池,大小为3  
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(3);  
  
        // 延迟3秒后执行一次任务  
        scheduledThreadPool.schedule(new Runnable() {  
            @Override  
            public void run() {  
                System.out.println("延迟三秒");  
            }  
        }, 3, TimeUnit.SECONDS);  
  
        // 初始延迟1秒后,每三秒执行一次任务  
        scheduledThreadPool.scheduleAtFixedRate(new Runnable() {  
            @Override  
            public void run() {  
                System.out.println("延迟1秒后每三秒执行一次");  
            }  
        }, 1, 3, TimeUnit.SECONDS);  
  
        // 注意:通常在实际应用中,您可能需要保持对ScheduledExecutorService的引用,  
        // 并在不再需要时调用shutdown()或shutdownNow()来关闭线程池。  
        // 例如:  
        // scheduledThreadPool.shutdown(); // 当不再需要线程池时调用  

4. newSingleThreadExecutor:单线程化的线程池。

  • 特点:只有一个线程来执行任务,保证所有任务按照指定顺序(FIFO,先进先出)执行。

这个线程池可以在线程死后(或发生异常时)重新启动一个线程来替代原来的线程继续执行下去。

  • 使用场景:适用于需要保证任务顺序执行的场景,如数据导入、日志处理等。
  • 示例:
ExecutorService executorService = Executors.newSingleThreadExecutor();

四、JAVA 线程生命周期(状态)

在Java中,线程的生命周期可以描述为一系列的状态转换。线程的状态是由线程类(java.lang.Thread)中的枚举类型Thread.State来定义的。以下是线程可能经历的主要状态:

1. NEW(新建):

  • 当程序使用new关键字创建了一个线程之后,该线程就处于新建状态,此时仅由JVM为其分配内存,并初始化其成员变量的值;
  • 此时,线程还没有开始执行。可以通过调用线程的start()方法使其进入就绪状态。

2. RUNNABLE(就绪状态):

  • 当线程调用start()方法后,它进入RUNNABLE状态。
  • 线程调度器负责将处于RUNNABLE状态的线程转换为运行状态,并分配CPU时间片给它执行。
  • 如果线程在run()方法中调用了一个阻塞的I/O操作,或者调用了sleep(), wait(), join()等使线程暂停的方法,线程将不会立即执行。但是,从线程状态的角度看,它仍然处于RUNNABLE状态。

3.运行状态(RUNNING):

如果处于就绪状态的线程获得了CPU,开始执行run()方法的线程执行体,则该线程处于运行状态。

4. BLOCKED(阻塞):

阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。

阻塞的情况分三种:

  • 等待阻塞(o.wait->等待对列): 运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
  • 同步阻塞(lock->锁池) :运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
  • 其他阻塞(sleep/join) :运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

5.线程死亡(DEAD):

线程会以下面三种方式结束,结束后就是死亡状态。

  1. 正常结束
    run()或call()方法执行完成,线程正常结束。

  2. 异常结束
    线程抛出一个未捕获的Exception或Error。

  3. 调用stop
    直接调用该线程的stop()方法来结束该线程—该方法通常容易导致死锁,不推荐使用。

在这里插入图片描述

五、Java 线程方法

线程在Java中的常用方法主要包括以下几种:

  • start():
    启动线程,使线程进入就绪状态(Runnable),等待获取CPU的执行权。当线程对象调用此方法时,Java虚拟机调用该线程的run()方法。
  • run():
    线程在被调度时执行的操作。通常我们会在自己的线程类中重写此方法,包含线程需要执行的任务。
  • sleep(long millis):
    使当前线程(即调用该方法的线程)暂停执行一段时间(以毫秒为单位),让其他线程有机会继续执行。但此方法不会释放对象锁。调用此方法时需要处理InterruptedException。
  • yield():
    暂停当前正在执行的线程对象,并执行其他线程。但具体哪个线程会执行是取决于操作系统的线程调度器的。此方法只能让同优先级的线程有执行的机会。
  • join():
    在线程A中调用线程B的join()方法,则线程A会等待线程B执行完毕后,再继续执行。这常常用于确保某些线程(如后台工作线程)在其他线程(如主线程)之前结束。
  • interrupt():
    中断线程。线程的中断并不是强制停止线程,而是设置一个中断状态。线程需要自行检查该状态,并据此决定是否停止执行。
  • isInterrupted():
    检测当前线程是否已经中断。
  • isAlive():
    测试线程是否处于活动状态。当线程启动后、尚未终止之前,即处于活动状态。
  • currentThread():
    返回对当前正在执行的线程对象的引用。
  • wait()、notify()、notifyAll():
    这三个方法都必须在同步方法或同步代码块中使用,因为它们是Java对象锁的一部分
    wait()会使当前线程等待直到其他线程调用此对象的notify()方法或notifyAll()方法
    notify()会唤醒在此对象监视器上等待的单个线程
    notifyAll()会唤醒在此对象监视器上等待的所有线程

需要注意的是,虽然suspend()和resume()这两个方法也曾用于控制线程的执行,但它们因为容易导致死锁,已经被废弃,不再推荐使用

  • 45
    点赞
  • 52
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值