自己的九又四分之三站台

自己的九又四分之三站台

在Java中,多线程编程是一种强大的并发编程技术,可以让你同时执行多个任务。Java提供了多种方式来创建和管理线程。以下是Java中给多线程使用的一些主要方法:

  1. 继承Thread

    • 创建一个新的类继承自Thread类。
    • 覆盖run()方法以定义线程的执行行为。
    • 创建该类的实例,并调用其start()方法来启动线程。
  2. 实现Runnable接口

    • 创建一个类实现Runnable接口,并实现run()方法。
    • 创建Thread类的实例,将实现了Runnable接口的类的实例作为参数传给Thread的构造函数。
    • 调用Thread实例的start()方法来启动线程。
  3. 使用Executor框架

    • Executor框架提供了一种高级的线程管理和任务调度机制。
    • 可以通过Executors类的静态工厂方法创建不同类型的线程池,如newFixedThreadPoolnewCachedThreadPool等。
    • 使用线程池,可以通过execute()方法提交实现了Runnable接口的任务,或者通过submit()方法提交实现了Callable接口的任务。
  4. 使用FutureCallable

    • Callable接口类似于Runnable,不同之处在于Callable可以返回结果并能抛出异常。
    • 提交Callable任务给ExecutorService后,会返回一个Future对象,通过这个对象可以获取任务执行结果或取消任务。
  5. 使用ForkJoin框架

    • ForkJoin框架专为大规模并行计算设计,它利用工作窃取算法来提高CPU的利用率。
    • 继承RecursiveTask(有返回值)或RecursiveAction(无返回值),实现它们的compute方法来定义分支和合并的逻辑。
    • 创建ForkJoinPool实例并提交ForkJoinTask

以上是Java中实现多线程的一些常用方法及同步机制。根据具体的应用场景和需求,开发者可以选择适合的方法来实现并发编程。

1.Thread线程使用方法

Thread是Java中实现多线程程序的基本单位。每个Thread对象都代表了一个线程的实例。Java中的线程是通过java.lang.Thread类来实现的。Thread类本身实现了Runnable接口,允许你覆盖其run()方法,定义线程执行的任务。

  • Thread类是实现线程的一种方式。每个Thread对象代表一个线程的实例。
  • 通过继承Thread类并重写其run()方法来创建一个新的线程。run()方法定义了线程的执行行为。
  • 创建Thread类的实例后,可以调用其start()方法来启动线程,这会导致run()方法的执行。

1.1.Thread的核心特性

  • 简单性:直接继承Thread类并覆盖run()方法,就可以定义线程的任务。
  • 直接控制:由于是直接操作线程对象,可以很容易地管理和控制线程的状态(如开始、暂停、继续、中断)。
  • 限制性:由于Java是单继承的,如果你的类已经继承了另一个类,就不能通过继承Thread类的方式来创建线程。

1.2.代码样例

  
public class MyThread extends Thread {  
  
    public MyThread(String threadName){  
        this.setName(threadName);  
    }  
  
    public void run() {  
        for (int i = 0; i < 20; i++) {  
            // 打印正在执行的线程名称  
            System.out.println(Thread.currentThread().getName() + " Value " + i);  
        }  
    }  
}

  
public class ThreadExample  {  
    public static void runDemo(){  
        // 创建线程  
        MyThread t1 = new MyThread("线程1");  
  
        MyThread t2 = new MyThread("线程2");  
  
        // 启动线程  
        t1.start();  
        t2.start();  
    }  
}

public class DemoMain {  
    public static void main(String[] args) {  
        ThreadExample.runDemo();  
    }  
}
  • 输出结果
线程1 Value 0
线程2 Value 0
线程2 Value 1
线程2 Value 2
线程2 Value 3
线程2 Value 4
线程2 Value 5
线程1 Value 1
线程2 Value 6
线程1 Value 2
线程2 Value 7
线程1 Value 3
线程2 Value 8
线程1 Value 4
线程2 Value 9
线程1 Value 5
线程2 Value 10
线程1 Value 6
线程2 Value 11
线程1 Value 7
线程2 Value 12
线程1 Value 8
线程2 Value 13
线程1 Value 9
线程2 Value 14
线程1 Value 10
线程2 Value 15
线程1 Value 11
线程2 Value 16
线程1 Value 12
线程2 Value 17
线程1 Value 13
线程2 Value 18
线程1 Value 14
线程2 Value 19
线程1 Value 15
线程1 Value 16
线程1 Value 17
线程1 Value 18
线程1 Value 19

1.3.Thread的其他细节操作

1.3.1.等待线程结束(join)

class ThreadJoinExample extends Thread {
    public void run() {
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(500); // 模拟执行任务
                System.out.println(Thread.currentThread().getName() + " index=" + i);
            } catch (InterruptedException e) {
                System.out.println("Thread interrupted.");
            }
        }
    }

    public static void main(String[] args) {
        ThreadJoinExample t1 = new ThreadJoinExample();
        ThreadJoinExample t2 = new ThreadJoinExample();

        t1.start();

        try {
            t1.join(); // 等待t1执行完成
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t2.start();
    }
}

1.3.2.中断线程(interrupt)

  • interrupt()方法用于中断线程。如果线程因为调用Object.wait()Thread.join()Thread.sleep()方法而被阻塞,它将抛出一个InterruptedException
  • 线程可以通过检查中断状态来优雅地终止执行。
class ThreadInterruptExample extends Thread {
    public void run() {
        try {
            for (int i = 0; i < 5; i++) {
                Thread.sleep(1000); // 模拟执行任务
                System.out.println(Thread.currentThread().getName() + " index=" + i);
            }
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + " Interrupted");
        }
    }

    public static void main(String[] args) {
        ThreadInterruptExample t1 = new ThreadInterruptExample();
        t1.start();

        try {
            Thread.sleep(3000); // 让t1有足够的时间运行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t1.interrupt(); // 请求中断t1线程
    }
}

1.3.3.设置和检查线程的优先级

  • 线程的优先级通过整数表示,范围从Thread.MIN_PRIORITY(1)到Thread.MAX_PRIORITY(10)。默认优先级为Thread.NORM_PRIORITY(5)。
  • 可以使用setPriority(int)方法来设置线程的优先级,并使用getPriority()来检查优先级。
class ThreadPriorityExample extends Thread {
    public void run() {
        System.out.println(Thread.currentThread().getName() + " Priority: " + Thread.currentThread().getPriority());
    }

    public static void main(String[] args) {
        ThreadPriorityExample t1 = new ThreadPriorityExample();
        ThreadPriorityExample t2 = new ThreadPriorityExample();

        t1.setPriority(Thread.MIN_PRIORITY);
        t2.setPriority(Thread.MAX_PRIORITY);

        t1.start();
        t2.start();
    }
}

1.3.4.守护线程(Daemon Thread)

  • 守护线程是一种在后台运行的线程,当所有的非守护线程都结束时,守护线程会自动终止。
  • 可以通过setDaemon(true)方法将线程设置为守护线程,默认情况下,线程是非守护线程。
Thread daemonThread = new Thread(() -> {
    while (true) {
        // 执行后台任务
    }
});
daemonThread.setDaemon(true); // 将线程设置为守护线程
daemonThread.start();

1.3.5.线程睡眠(Sleep)

  • Thread.sleep()方法使当前线程暂停执行一段时间,以毫秒为单位。
  • 睡眠期间,线程不会释放锁,因此其他线程无法访问被当前线程持有的锁。
try {
    Thread.sleep(1000); // 线程睡眠1秒
} catch (InterruptedException e) {
    e.printStackTrace();
}

1.3.6.线程等待(Wait)与唤醒(Notify)

  • wait()方法使线程进入等待状态,直到其他线程调用相同对象上的notify()notifyAll()方法唤醒它。
  • 必须在同步块中使用wait()notify()方法,否则会抛出IllegalMonitorStateException异常。
synchronized (object) {
    object.wait(); // 线程等待
}
synchronized (object) {
    object.notify(); // 唤醒一个等待线程
    // 或者使用 object.notifyAll(); 唤醒所有等待线程
}

1.3.7.注意事项

  • 避免使用Thread.stop():由于它是不安全的,Java已经弃用了stop()方法来停止线程。它会导致线程立即终止,而不会进行任何清理操作,可能会产生不一致的状态。应该使用中断和检查机制来优雅地终止线程。
  • 优先级只是给线程调度器一个提示:线程的优先级并不能保证执行顺序或者执行时间。线程调度器可能会忽略这些优先级,特别是在不同的操作系统上。
  • 中断是一种协作机制:线程需要定期检查其中断状态,并相应地优雅地终止。

2.Runnable线程使用方法

2.1.Runnable的核心特性

Runnable接口是Java中用于表示可运行任务的标准接口,它是实现多线程的一种方式。其核心特性如下:

  1. 抽象任务Runnable接口定义了一个抽象方法run(),用于表示具体的任务逻辑。实现Runnable接口的类需要实现run()方法,将具体的任务逻辑放在其中。

  2. 无返回值run()方法没有返回值,它是一个void类型的方法。因此,Runnable接口表示的是一种无返回值的任务。

  3. 多线程支持:实现Runnable接口的类可以被多个线程共享,多个线程可以同时执行同一个Runnable对象的run()方法。

  4. 线程与任务解耦Runnable接口将任务的定义与线程的实现解耦,使得任务可以在不同的线程中执行,从而提高了代码的灵活性和可复用性。

  5. 可与Thread类结合使用Runnable接口通常与Thread类结合使用,通过创建一个Thread对象并将Runnable对象作为参数传递给Thread对象的构造方法,来创建并启动一个新线程。

  6. 适用性广泛:由于其简单、灵活的特性,Runnable接口适用于各种需要并发执行的任务,可以用于各种场景,包括但不限于并行计算、异步处理、任务调度等。

综上所述,Runnable接口是实现多线程的一种基本方式,它定义了一个抽象的任务,并提供了一种通用的方式来表示可运行的任务,适用于各种需要并发执行的场景。

2.2. 代码样例

public class MyRunnable implements Runnable {  
    public void run() {  
        for (int i = 0; i < 20; i++) {  
            System.out.println(Thread.currentThread().getName() + " Value " + i);  
        }  
    }  
}
public class RunnableExample {  
    public static void runDemo(){  
        // 创建Runnable实例  
        MyRunnable myRunnable = new MyRunnable();  
  
        // 创建Thread对象,并将Runnable实例作为目标对象传递给Thread构造器  
        Thread t1 = new Thread(myRunnable);  
        Thread t2 = new Thread(myRunnable);  
  
        // 启动线程  
        t1.start();  
        t2.start();  
    }  
}
  • 输出结果
Thread-0 Value 0
Thread-1 Value 0
Thread-0 Value 1
Thread-0 Value 2
Thread-0 Value 3
Thread-1 Value 1
Thread-1 Value 2
Thread-1 Value 3
Thread-1 Value 4
Thread-0 Value 4
Thread-0 Value 5
Thread-0 Value 6
Thread-0 Value 7
Thread-0 Value 8
Thread-0 Value 9
Thread-0 Value 10
Thread-0 Value 11
Thread-0 Value 12
Thread-0 Value 13
Thread-0 Value 14
Thread-0 Value 15
Thread-1 Value 5
Thread-0 Value 16
Thread-1 Value 6
Thread-0 Value 17
Thread-1 Value 7
Thread-0 Value 18
Thread-1 Value 8
Thread-0 Value 19
Thread-1 Value 9
Thread-1 Value 10
Thread-1 Value 11
Thread-1 Value 12
Thread-1 Value 13
Thread-1 Value 14
Thread-1 Value 15
Thread-1 Value 16
Thread-1 Value 17
Thread-1 Value 18
Thread-1 Value 19

3.Thread和Runnable 区别

ThreadRunnable是实现多线程程序的两种基本方式,它们各有特点和适用场景。以下是ThreadRunnable之间的主要区别:

3.1. 继承与实现的区别

  • Thread:是一个类,继承Thread类来创建线程意味着你的线程类不能再继承其他类,因为Java不支持多重继承。
  • Runnable:是一个接口,实现Runnable接口如果你的类已经继承了另一个类,你仍然可以通过实现Runnable接口来创建线程。

3.2. 设计灵活性

  • Thread:直接继承Thread类可能会限制类的扩展性,因为你的类已经继承了Thread,它不能再继承其他类。
  • Runnable:实现Runnable接口更加灵活,它允许你的类继承其他类,在多个线程间共享相同的资源。

3.3. 资源共享

  • Thread:每个Thread类的实例都是一个单独的对象,它们之间的资源不共享,除非显式地使用外部资源。
  • Runnable:多个线程可以共享同一个Runnable实例,因此它们可以访问相同的资源,这使得在多个线程之间共享数据和行为变得更加容易。

3.4. 适用场景

  • Thread:当你的线程不需要访问共享资源或你只是创建少量线程时,继承Thread类可能是一个简单的选择。
  • Runnable:当你需要多个线程共享数据或资源,或者你的类已经继承了另一个类时,实现Runnable接口是更好的选择。

3.5. 推荐做法

虽然两种方式都可以用来创建线程,但实践中更推荐实现Runnable接口,原因包括:

  • 实现Runnable接口的方式更加灵活,允许继承其他类并实现多个接口。
  • 它支持多个线程共享同一个Runnable实例,便于线程间的资源共享。
  • Executor框架结合使用时,通常要求提供RunnableCallable任务,而不是Thread对象。

总的来说,虽然直接使用Thread类可以简化某些编程场景,但实现Runnable接口提供了更高的灵活性和资源共享的能力,因此在多数情况下更被推荐。

4.Executor线程池的使用方法

Executor是Java中用于执行任务的框架,它提供了一种将任务提交与任务执行分离的方式,可以更好地管理和控制线程的创建和执行。Executor框架使得并发编程变得更加简单和可控,可以有效地利用系统资源,提高程序的性能和可扩展性。

4.1.Executor框架的核心组件

  1. Executor接口Executor是一个接口,它定义了单一的方法execute(Runnable command),用于执行给定的任务。

  2. ExecutorService接口ExecutorServiceExecutor的子接口,它扩展了Executor接口,提供了更丰富的功能。主要包括:

    • 提交任务并返回Future对象,用于追踪任务的执行状态和结果。
    • 管理线程池的生命周期,包括启动、关闭、终止等。
  3. ThreadPoolExecutor类ThreadPoolExecutorExecutorService接口的一个具体实现,它是一个灵活的线程池实现,可以根据需要自动调整线程数量。

  4. Executors工厂类Executors类是一个工厂类,提供了创建各种类型线程池的静态方法,如newFixedThreadPoolnewCachedThreadPoolnewSingleThreadExecutor等。

  5. ScheduledExecutorService接口ScheduledExecutorServiceExecutorService的一个子接口,它提供了可以延迟执行或定期执行任务的功能。

4.2.Executor框架的优点

  1. 简化并发编程Executor框架将任务的提交和执行分离开来,简化了并发编程的复杂性。
  2. 提高资源利用率:通过使用线程池,可以重复利用已创建的线程,减少了线程的创建和销毁开销。
  3. 提高性能:通过并行执行多个任务,可以加快任务的执行速度,提高程序的性能。
  4. 提高可管理性Executor框架提供了一系列方法来管理线程池的状态和行为,如监控线程池的执行状态、设置线程池的大小等。

4.3.代码样例

public class CustomThreadFactory implements ThreadFactory {  
    private String threadNamePrefix;  
  
    public CustomThreadFactory(String threadNamePrefix) {  
        this.threadNamePrefix = threadNamePrefix;  
    }  
  
    @Override  
    public Thread newThread(Runnable r) {  
        Thread thread = new Thread(r);  
        // 设置线程名称  
        thread.setName(threadNamePrefix + "-" + thread.getId());  
        return thread;  
    }  
}
  
public class ExecutorExample {  
    public static void runDemo() {  
        // 创建自定义的ThreadFactory  
        ThreadFactory threadFactory = new CustomThreadFactory("Xiaocai_CustomThread");  
  
        // 创建固定大小的线程池  
        ExecutorService executor = Executors.newFixedThreadPool(5,threadFactory);  
  
        // 提交任务  
        for (Integer i = 0; i < 10; i++) {  
            final Integer threadIndex=i;  
            executor.execute(new MyThread(threadIndex.toString()));  
        }  
        System.out.println("关闭线程池之前");  
        // 关闭线程池  
        executor.shutdown();  
        System.out.println("关闭线程池之后");  
  
        try {  
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("线程池中所有线程执行完毕");  
  
    }  
}
  • 输出结果
已连接到目标 VM, 地址: ''127.0.0.1:14181',传输: '套接字''
Xiaocai_CustomThread-12 Value 0
关闭线程池之前
Xiaocai_CustomThread-14 Value 0
Xiaocai_CustomThread-14 Value 1
Xiaocai_CustomThread-14 Value 2
Xiaocai_CustomThread-14 Value 3
Xiaocai_CustomThread-14 Value 4
Xiaocai_CustomThread-16 Value 0
Xiaocai_CustomThread-16 Value 1
Xiaocai_CustomThread-16 Value 2
Xiaocai_CustomThread-16 Value 3
Xiaocai_CustomThread-12 Value 1
Xiaocai_CustomThread-12 Value 2
Xiaocai_CustomThread-12 Value 3
Xiaocai_CustomThread-12 Value 4
Xiaocai_CustomThread-12 Value 5
Xiaocai_CustomThread-12 Value 6
关闭线程池之后
Xiaocai_CustomThread-16 Value 4
Xiaocai_CustomThread-14 Value 5
Xiaocai_CustomThread-14 Value 6
Xiaocai_CustomThread-16 Value 5
Xiaocai_CustomThread-16 Value 6
Xiaocai_CustomThread-16 Value 7
Xiaocai_CustomThread-16 Value 8
Xiaocai_CustomThread-16 Value 9
Xiaocai_CustomThread-18 Value 0
Xiaocai_CustomThread-20 Value 0
Xiaocai_CustomThread-20 Value 1
Xiaocai_CustomThread-12 Value 7
Xiaocai_CustomThread-20 Value 2
Xiaocai_CustomThread-18 Value 1
Xiaocai_CustomThread-16 Value 10
Xiaocai_CustomThread-16 Value 11
Xiaocai_CustomThread-14 Value 7
Xiaocai_CustomThread-14 Value 8
Xiaocai_CustomThread-14 Value 9
Xiaocai_CustomThread-16 Value 12
Xiaocai_CustomThread-18 Value 2
Xiaocai_CustomThread-18 Value 3
Xiaocai_CustomThread-18 Value 4
Xiaocai_CustomThread-20 Value 3
Xiaocai_CustomThread-12 Value 8
Xiaocai_CustomThread-20 Value 4
Xiaocai_CustomThread-18 Value 5
Xiaocai_CustomThread-16 Value 13
Xiaocai_CustomThread-14 Value 10
Xiaocai_CustomThread-16 Value 14
Xiaocai_CustomThread-18 Value 6
Xiaocai_CustomThread-20 Value 5
Xiaocai_CustomThread-12 Value 9
Xiaocai_CustomThread-20 Value 6
Xiaocai_CustomThread-18 Value 7
Xiaocai_CustomThread-16 Value 15
Xiaocai_CustomThread-14 Value 11
Xiaocai_CustomThread-16 Value 16
Xiaocai_CustomThread-18 Value 8
Xiaocai_CustomThread-20 Value 7
Xiaocai_CustomThread-12 Value 10
Xiaocai_CustomThread-20 Value 8
Xiaocai_CustomThread-18 Value 9
Xiaocai_CustomThread-16 Value 17
Xiaocai_CustomThread-14 Value 12
Xiaocai_CustomThread-16 Value 18
Xiaocai_CustomThread-18 Value 10
Xiaocai_CustomThread-20 Value 9
Xiaocai_CustomThread-12 Value 11
Xiaocai_CustomThread-20 Value 10
Xiaocai_CustomThread-18 Value 11
Xiaocai_CustomThread-16 Value 19
Xiaocai_CustomThread-14 Value 13
Xiaocai_CustomThread-18 Value 12
Xiaocai_CustomThread-20 Value 11
Xiaocai_CustomThread-12 Value 12
Xiaocai_CustomThread-20 Value 12
Xiaocai_CustomThread-18 Value 13
Xiaocai_CustomThread-16 Value 0
Xiaocai_CustomThread-14 Value 14
Xiaocai_CustomThread-16 Value 1
Xiaocai_CustomThread-18 Value 14
Xiaocai_CustomThread-20 Value 13
Xiaocai_CustomThread-12 Value 13
Xiaocai_CustomThread-20 Value 14
Xiaocai_CustomThread-18 Value 15
Xiaocai_CustomThread-16 Value 2
Xiaocai_CustomThread-14 Value 15
Xiaocai_CustomThread-16 Value 3
Xiaocai_CustomThread-18 Value 16
Xiaocai_CustomThread-20 Value 15
Xiaocai_CustomThread-12 Value 14
Xiaocai_CustomThread-20 Value 16
Xiaocai_CustomThread-18 Value 17
Xiaocai_CustomThread-16 Value 4
Xiaocai_CustomThread-14 Value 16
Xiaocai_CustomThread-16 Value 5
Xiaocai_CustomThread-18 Value 18
Xiaocai_CustomThread-20 Value 17
Xiaocai_CustomThread-12 Value 15
Xiaocai_CustomThread-20 Value 18
Xiaocai_CustomThread-18 Value 19
Xiaocai_CustomThread-16 Value 6
Xiaocai_CustomThread-14 Value 17
Xiaocai_CustomThread-16 Value 7
Xiaocai_CustomThread-18 Value 0
Xiaocai_CustomThread-20 Value 19
Xiaocai_CustomThread-12 Value 16
Xiaocai_CustomThread-20 Value 0
Xiaocai_CustomThread-18 Value 1
Xiaocai_CustomThread-16 Value 8
Xiaocai_CustomThread-14 Value 18
Xiaocai_CustomThread-16 Value 9
Xiaocai_CustomThread-18 Value 2
Xiaocai_CustomThread-20 Value 1
Xiaocai_CustomThread-12 Value 17
Xiaocai_CustomThread-20 Value 2
Xiaocai_CustomThread-18 Value 3
Xiaocai_CustomThread-16 Value 10
Xiaocai_CustomThread-14 Value 19
Xiaocai_CustomThread-16 Value 11
Xiaocai_CustomThread-18 Value 4
Xiaocai_CustomThread-20 Value 3
Xiaocai_CustomThread-12 Value 18
Xiaocai_CustomThread-20 Value 4
Xiaocai_CustomThread-18 Value 5
Xiaocai_CustomThread-16 Value 12
Xiaocai_CustomThread-14 Value 0
Xiaocai_CustomThread-16 Value 13
Xiaocai_CustomThread-18 Value 6
Xiaocai_CustomThread-20 Value 5
Xiaocai_CustomThread-12 Value 19
Xiaocai_CustomThread-12 Value 0
Xiaocai_CustomThread-20 Value 6
Xiaocai_CustomThread-18 Value 7
Xiaocai_CustomThread-16 Value 14
Xiaocai_CustomThread-14 Value 1
Xiaocai_CustomThread-16 Value 15
Xiaocai_CustomThread-18 Value 8
Xiaocai_CustomThread-20 Value 7
Xiaocai_CustomThread-12 Value 1
Xiaocai_CustomThread-20 Value 8
Xiaocai_CustomThread-18 Value 9
Xiaocai_CustomThread-16 Value 16
Xiaocai_CustomThread-14 Value 2
Xiaocai_CustomThread-16 Value 17
Xiaocai_CustomThread-18 Value 10
Xiaocai_CustomThread-20 Value 9
Xiaocai_CustomThread-12 Value 2
Xiaocai_CustomThread-20 Value 10
Xiaocai_CustomThread-18 Value 11
Xiaocai_CustomThread-16 Value 18
Xiaocai_CustomThread-14 Value 3
Xiaocai_CustomThread-16 Value 19
Xiaocai_CustomThread-18 Value 12
Xiaocai_CustomThread-20 Value 11
Xiaocai_CustomThread-12 Value 3
Xiaocai_CustomThread-20 Value 12
Xiaocai_CustomThread-18 Value 13
Xiaocai_CustomThread-14 Value 4
Xiaocai_CustomThread-18 Value 14
Xiaocai_CustomThread-20 Value 13
Xiaocai_CustomThread-12 Value 4
Xiaocai_CustomThread-20 Value 14
Xiaocai_CustomThread-18 Value 15
Xiaocai_CustomThread-14 Value 5
Xiaocai_CustomThread-18 Value 16
Xiaocai_CustomThread-20 Value 15
Xiaocai_CustomThread-12 Value 5
Xiaocai_CustomThread-20 Value 16
Xiaocai_CustomThread-18 Value 17
Xiaocai_CustomThread-14 Value 6
Xiaocai_CustomThread-18 Value 18
Xiaocai_CustomThread-20 Value 17
Xiaocai_CustomThread-12 Value 6
Xiaocai_CustomThread-20 Value 18
Xiaocai_CustomThread-18 Value 19
Xiaocai_CustomThread-14 Value 7
Xiaocai_CustomThread-20 Value 19
Xiaocai_CustomThread-12 Value 7
Xiaocai_CustomThread-12 Value 8
Xiaocai_CustomThread-14 Value 8
Xiaocai_CustomThread-12 Value 9
Xiaocai_CustomThread-14 Value 9
Xiaocai_CustomThread-12 Value 10
Xiaocai_CustomThread-14 Value 10
Xiaocai_CustomThread-12 Value 11
Xiaocai_CustomThread-14 Value 11
Xiaocai_CustomThread-12 Value 12
Xiaocai_CustomThread-14 Value 12
Xiaocai_CustomThread-12 Value 13
Xiaocai_CustomThread-14 Value 13
Xiaocai_CustomThread-12 Value 14
Xiaocai_CustomThread-14 Value 14
Xiaocai_CustomThread-12 Value 15
Xiaocai_CustomThread-14 Value 15
Xiaocai_CustomThread-12 Value 16
Xiaocai_CustomThread-14 Value 16
Xiaocai_CustomThread-12 Value 17
Xiaocai_CustomThread-14 Value 17
Xiaocai_CustomThread-12 Value 18
Xiaocai_CustomThread-14 Value 18
Xiaocai_CustomThread-12 Value 19
Xiaocai_CustomThread-14 Value 19
线程池中所有线程执行完毕
与目标 VM 断开连接, 地址为: ''127.0.0.1:14181',传输: '套接字''

进程已结束,退出代码为 0

在上面的示例中,通过Executors.newFixedThreadPool(5)方法创建了一个固定大小为5的线程池,然后提交了10个任务。线程池会自动管理这些任务的执行,使用合适数量的线程执行任务。最后通过executor.shutdown()方法关闭线程池。

总之,Executor框架提供了一种高级的、灵活的方式来执行任务,是Java并发编程中的重要工具之一,值得深入学习和应用。

4.4.细节操作

4.4.1.怎么判断线程池中所有线程全部运行完毕呢?

		System.out.println("关闭线程池之前");  
        // 关闭线程池  
        executor.shutdown();  
        System.out.println("关闭线程池之后");  
  
        try {  
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("线程池中所有线程执行完毕");  

4.4.2.调用executor.shutdown()的目的

调用executor.shutdown()方法的目的是关闭线程池,即停止接受新的任务,并尝试将已提交但尚未开始执行的任务从任务队列中移除,同时等待已经提交的任务执行完毕。主要原因包括:

  1. 释放资源:线程池在执行完所有任务后,会保持活动状态,并且等待新的任务。如果不关闭线程池,线程池中的线程将一直存在,占用系统资源(如内存等),可能导致资源浪费。

  2. 避免任务丢失:如果不关闭线程池,那么主程序执行完毕后可能会立即结束,导致尚未执行完的任务丢失。

  3. 优雅关闭:调用shutdown()方法后,线程池不会立即关闭,而是等待之前提交的任务执行完成后关闭,保证已提交的任务能够被执行完毕,避免任务中途被打断。

  4. 避免线程泄露:如果不关闭线程池,线程池中的线程可能会一直存在,即使主程序已经执行完毕,导致线程泄露问题。

因此,一般情况下,在不再需要线程池时应该及时调用shutdown()方法来关闭线程池,以释放资源并确保已提交的任务能够正常执行完毕。

5.Future使用方法

Future是Java中表示异步计算结果的接口,它提供了一种机制,可以在任务执行完成之前先提交任务,并且在需要结果时获取任务执行的结果。通过Future,可以在任务执行的过程中进行其他操作,而不必阻塞等待任务执行完成。

5.1.Future的核心特性

  1. 表示异步计算结果Future接口表示一个异步计算的结果。可以通过get()方法来获取计算的结果,如果计算尚未完成,则会阻塞直到计算完成。

  2. 取消任务Future提供了cancel()方法用于取消任务的执行。如果任务已经完成,或者由于其他原因无法取消,则cancel()方法返回false

  3. 判断任务状态isDone()方法用于判断任务是否完成,isCancelled()方法用于判断任务是否被取消。

5.2.代码样例

public class FutureExample {  
    public static void runDemo() {  
        // 创建线程池  
        ExecutorService executor = Executors.newFixedThreadPool(1);  
  
        // 提交任务并获取Future对象  
        Future<Integer> future = executor.submit(new Callable<Integer>() {  
            @Override  
            public Integer call() throws Exception {  
                // 模拟一个耗时的任务  
                Thread.sleep(2000);  
                return 42;  
            }  
        });  
  
        // 其他操作...  
        System.out.println("Do something else while waiting for the result...");  
  
        try {  
            // 获取任务结果,如果任务尚未完成,会阻塞直到任务完成  
            Integer result = future.get();  
            System.out.println("Result: " + result);  
        } catch (InterruptedException | ExecutionException e) {  
            e.printStackTrace();  
        }  
  
        // 关闭线程池  
        executor.shutdown();  
    }  
}

6.Callable使用方法

Callable是Java中用于表示可调用任务的接口,与Runnable接口类似,但它可以返回结果并且能够抛出异常。通常与ExecutorService一起使用,通过submit(Callable)方法提交Callable任务,并返回一个Future对象,用于获取任务的执行结果。

6.1.Callable的核心特性

  1. 返回结果Callable接口使用泛型来定义返回类型,可以通过call()方法返回计算的结果。

  2. 抛出异常call()方法可以抛出异常,允许任务执行过程中发生异常情况。

6.2.使用示例

public class CallableExample {  
    public static void runDemo() {  
        // 创建线程池  
        ExecutorService executor = Executors.newFixedThreadPool(1);  
  
        // 提交Callable任务并获取Future对象  
        Future<Integer> future = executor.submit(new MyCallable());  
  
        // 其他操作...  
        System.out.println("Do something else while waiting for the result...");  
  
        try {  
            // 获取任务结果,如果任务尚未完成,会阻塞直到任务完成  
            Integer result = future.get();  
            System.out.println("Result: " + result);  
        } catch (InterruptedException | ExecutionException e) {  
            e.printStackTrace();  
        }  
  
        // 关闭线程池  
        executor.shutdown();  
    }  
}

public class MyCallable implements Callable<Integer> {  
    @Override  
    public Integer call() throws Exception {  
        // 模拟一个耗时的任务  
        Thread.sleep(2000);  
        return 42;  
    }  
}

在上面的示例中,首先创建了一个固定大小为1的线程池。然后通过executor.submit(Callable)方法提交一个Callable任务,并获得一个Future对象。在任务执行的过程中,可以执行其他操作,而不必等待任务完成。当需要任务结果时,调用future.get()方法,如果任务已经完成,则立即返回结果;如果任务尚未完成,则会阻塞直到任务完成,并返回结果。

总之,Callable接口允许在任务执行过程中返回结果和抛出异常,提供了一种更加灵活和功能丰富的任务定义方式,适用于需要异步计算结果的场景。

7.ForkJoin使用方法

ForkJoin框架是Java中用于并行计算的框架,它基于"分而治之"的思想,能够有效地利用多核处理器的优势,加速任务的执行速度。ForkJoin框架提供了ForkJoinPoolForkJoinTaskRecursiveTask等类来支持并行计算。

ForkJoin框架的核心特性

  1. 任务分解ForkJoin框架通过递归的方式将大任务划分为小任务,然后并行执行这些小任务,从而实现任务的分解和并行处理。

  2. 工作窃取ForkJoin框架中的ForkJoinPool使用工作窃取算法来管理线程池中的工作线程。当一个工作线程没有任务可执行时,它会从其他工作线程的任务队列中偷取任务来执行,从而实现负载均衡。

  3. 递归任务ForkJoinTaskForkJoin框架中表示任务的抽象类,它有两个主要子类:RecursiveActionRecursiveTask,分别用于没有返回值的任务和有返回值的任务。

使用示例

下面是一个使用ForkJoin框架计算数组元素之和的示例:

public class ForkJoinExample {  
    public static void runDemo() {  
        // 创建ForkJoinPool  
        ForkJoinPool pool = new ForkJoinPool();  
  
        // 创建一个大数组  
        int[] array = new int[1000];  
        for (int i = 0; i < array.length; i++) {  
            array[i] = i + 1;  
        }  
  
        // 创建ForkJoin任务  
        ForkJoinTask<Integer> task = new SumTask(array, 0, array.length);  
  
        // 提交任务并获取结果  
        Integer result = pool.invoke(task);  
  
        // 输出结果  
        System.out.println("Sum of array elements: " + result);  
  
        // 关闭线程池  
        pool.shutdown();  
    }  
}

class SumTask extends RecursiveTask<Integer> {
    private int[] array;
    private int start;
    private int end;

    public SumTask(int[] array, int start, int end) {
        this.array = array;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        if (end - start <= 10) {
            // 如果任务规模小于等于10,直接计算结果
            int sum = 0;
            for (int i = start; i < end; i++) {
                sum += array[i];
            }
            return sum;
        } else {
            // 否则将任务分解成两个子任务
            int mid = (start + end) / 2;
            SumTask leftTask = new SumTask(array, start, mid);
            SumTask rightTask = new SumTask(array, mid, end);

            // 并行执行子任务
            leftTask.fork();
            rightTask.fork();

            // 合并子任务的结果
            int leftResult = leftTask.join();
            int rightResult = rightTask.join();

            // 返回结果
            return leftResult + rightResult;
        }
    }
}

在上面的示例中,首先创建了一个ForkJoinPool,然后创建了一个包含1000个元素的数组。接着创建了一个ForkJoinTask(这里使用了RecursiveTask的子类SumTask),并将其提交给ForkJoinPool进行执行。SumTask任务负责计算数组的元素之和,如果任务规模较小,则直接计算结果;否则将任务分解成两个子任务,分别计算子数组的元素之和,然后合并结果。

总之,ForkJoin框架通过任务的分解和并行执行,能够提高任务的执行效率,特别适用于处理大规模的数据并且任务之间存在递归关系的场景。

8.对比一下Thread、Runnable、Executor、Future、Callable、ForkJoin这几种线程操作的方式,对比他们之间的异同点,以及使用场景

下面是对Thread、Runnable、Executor、Future、Callable和ForkJoin这几种线程操作方式的异同点以及使用场景的对比:

8.1. Thread

  • 方式:直接继承Thread类,覆写run()方法。
  • 异同点:与其他方式相比,使用起来更为简单直观,但线程和任务的耦合性较强,不利于任务的复用和资源的管理。
  • 使用场景:适用于简单的并发任务,不需要共享资源或状态的场景。

8.2. Runnable

  • 方式:实现Runnable接口,实现run()方法。
  • 异同点:相比于直接继承Thread类,实现Runnable接口的方式更为灵活,可以更好地支持任务的复用和资源的管理。
  • 使用场景:适用于需要共享资源或状态的场景,也适用于已经继承其他类的情况下,无法再继承Thread类。

8.3. Executor

  • 方式:通过Executor框架来管理和执行任务。
  • 异同点:相比于直接创建线程,使用Executor框架能够更好地管理线程资源,提高线程的复用性和可管理性。
  • 使用场景:适用于需要管理和调度多个任务的场景,例如批量处理任务、任务队列等。

8.4. Future

  • 方式:通过Future接口来获取异步计算的结果。
  • 异同点Future接口提供了异步计算结果的获取方式,允许任务的提交和获取结果的操作分离开来,提高了程序的并发性能。
  • 使用场景:适用于需要等待异步任务完成并获取结果的场景,例如并行计算、I/O操作等。

8.5. Callable

  • 方式:实现Callable接口,实现call()方法。
  • 异同点:与Runnable接口类似,但Callable接口允许任务返回结果和抛出异常,功能更为丰富。
  • 使用场景:适用于需要任务返回结果或抛出异常的场景,例如需要等待任务执行完成并获取结果的场景。

8.6. ForkJoin

  • 方式:通过ForkJoin框架实现任务的并行计算。
  • 异同点:相比于传统的线程池,ForkJoin框架提供了任务分解和工作窃取等机制,能够更有效地利用多核处理器的优势。
  • 使用场景:适用于需要大规模并行计算的场景,例如递归任务的分解、归并排序等。

综上所述,不同的线程操作方式有不同的特点和适用场景。直接使用ThreadRunnable接口适合简单的并发任务;Executor框架适合管理和调度多个任务的场景;FutureCallable接口适合需要等待任务完成并获取结果的场景;ForkJoin框架适合需要大规模并行计算的场景。在实际应用中,根据具体的需求选择合适的线程操作方式能够更好地提高程序的性能和可维护性。

03-05 01:56