欢迎访问宙启技术站
智能推送

Java中如何使用线程(Thread)来实现并发编程?

发布时间:2023-06-15 16:12:49

Java是一种支持多线程编程的编程语言,线程(Thread)是Java中最基本的并发编程单元。线程在Java中被封装在Thread类中,通过创建Thread对象并调用start()方法即可创建一个新线程。通过Java中的线程机制,可以实现一些需要并发运行的任务,提高程序执行效率。

一、线程的创建和启动

Java中有两种方式来创建线程:

1.实现Runnable接口

在Java中,线程的类继承Thread类,而线程所要执行的任务则封装在Runnable接口的实现类中,线程类负责开辟线程和调用任务类中的run方法。实现Runnable接口需要实现其中的run()方法,这里需要注意的是,run()方法必须是public void类型。下面是一个简单的实现Runnable接口的示例:

public class MyRunnable implements Runnable{
    @Override
    public void run(){
        /*线程要执行的任务*/
    }
} 

创建Thread对象并调用start()方法实现线程的启动:

public static void main(String[] args){
    Thread thread = new Thread(new MyRunnable());
    thread.start();
}

2.继承Thread类

这种方式直接继承Thread类,实现多线程代码的对象和执行逻辑融为一体。它需要实现run()方法,在run()方法中编写线程执行的代码。

public class MyThread extends Thread{
    @Override
    public void run(){
        /*线程要执行的任务*/
    }
}

创建Thread对象并调用start()方法实现线程的启动:

public static void main(String[] args){
    Thread thread = new MyThread();
    thread.start();
}

二、线程的状态

在Java中,线程可以存在不同状态,线程的状态由JVM按照线程运行时的情况自动维护。以下是Java线程状态的分类:

1.新建状态(New)

当一个Thread对象被创建时,就处于新建状态。

2.运行状态(Runnable)

线程启动后,就处于运行状态。此时JVM会调用线程的run()方法。

3.阻塞状态(Blocked)

当线程睡眠(sleep)、等待(wait)或被其他线程阻塞时,就处于阻塞状态。

4.无限期等待状态(Waiting)

当线程执行无限期等待时(wait()),就处于无限期等待状态。

5.计时等待状态(Timed Waiting)

当线程在等待一段时间时,就处于计时等待状态(sleep(), join()等方法)。

6.终止状态(Terminated)

线程在执行完程序后进入终止状态。

三、线程的同步

在并发编程的环境下,多个线程可以同时访问同一个数据,造成读取数据时的不确定性,也会产生“死锁”等问题。为此,Java提供了同步机制来解决这类问题。Java中的同步机制一般使用synchronized关键字来实现线程之间的同步。

synchronized关键字可以有三种用法:

1.修饰方法:

在关键字后面加上方法声明,这样方式是将整个方法都加锁,调用该方法的线程将被阻塞。

public synchronized void method(){}

2.修饰块:

在关键字后面加上作用范围的对象,这样方式是将括号中的资源加锁。

synchronized(Object){/*需要同步的代码块*/}

3.修饰类:

在关键字后面加上class关键字和类,这样方式是将整个类加锁,调用该类的线程将被阻塞。

public class MyThread extends Thread{
    /*static synchronized方式*/
    public static synchronized void method(){}    
} 

四、线程的通信

线程在运行过程中,可能需要交互信息或协调运行,这时就需要用到线程之间的通信。Java中线程通信可以使用wait()、notify()、notifyAll()三个方法来实现。

1.wait()方法

该方法使线程进入等待状态,释放对象的锁。

2.notify()方法

该方法使等待中的一个线程从等待中恢复,并重新获取对象锁。

3.notifyAll()方法

该方法使等待中的所有线程从等待中恢复,并重新获取对象锁。

注意:在使用线程之间通信时,一定要使用synchronized关键字确保线程安全。

五、线程池的使用

线程池允许我们对线程的使用进行更好的管理。常见的线程池实现类有:FixedThreadPool,CachedThreadPool,SingleThreadPool。

1.创建FixedThreadPool

当使用FixedThreadPool时,我们需要指定每个线程需要处理的任务,这就是通过实现Runnable接口来实现的。假设我们需要处理100个任务,那么创建一个有10个线程的线程池:

ExecutorService executorService = Executors.newFixedThreadPool(10);
for(int i=0;i<100;i++){
    executorService.execute(new TaskObject());
}
executorService.shutdown();

我们使用ExecutorService.execute()方法将Runnable实例添加到线程池中,这样这些任务将会被线程池中的线程执行。

shutdown()方法可以使线程池中的任务执行完后关闭线程池。

2.创建CachedThreadPool

CachedThreadPool即可根据需要创建线程的线程池,线程数量无限制,如果不需要处理任务,它会自动清除线程。下面是CachedThreadPool的示例:

ExecutorService executorService = Executors.newCachedThreadPool();
for(int i=0;i<100;i++){
    executorService.execute(new TaskObject());
}
executorService.shutdown();

CachedThreadPool对临时任务做了很好的控制,不会浪费CPU时间。

3.创建SingleThreadPool

SingleThreadPool是只有一个线程的线程池,它可以保证任务的按顺序执行,这是非常有用的。下面是创建SingleThreadPool的示例:

ExecutorService executorService = Executors.newSingleThreadExecutor();
for(int i=0;i<100;i++){
    executorService.execute(new TaskObject());
}
executorService.shutdown();

运行结果一定是按顺序执行。可以看出SingleThreadPool非常适合需要顺序执行任务的场景。

六、总结

Java中的线程(Thread)是Java中最基本的并发编程单元,通过线程机制可以实现一些需要并发运行的任务,提高程序执行效率。在Java中,线程有多种创建方式,线程状态有多种分类,线程之间的同步和通信是多线程程序中必须要掌握的技能点。线程池是多线程编程中比较实用的工具,可以方便地管理线程和任务。在多线程编程中,需要注意线程的安全和资源的同步,避免出现竞争的问题。