Java创建多线程异步执行实现代码解析

Java创建多线程异步执行是很常见的开发需求,在实际开发过程中也经常用到,本篇文章将细致地讲解如何实现这一功能,包括创建多线程的方式、线程的基础操作、如何使用Java的Concurrent包以及线程安全的问题等问题。

1. 创建多线程

Java中创建多线程的方式有很多,这里主要介绍两种方式。

1.1 继承Thread类

第一种方式就是继承Thread类,并重写run方法。代码示例如下:

public class MyThread extends Thread {

    @Override
    public void run() {
        // 这里是线程执行的逻辑
    }
}

// 将线程启动
MyThread myThread = new MyThread();
myThread.start();

1.2 实现Runnable接口

第二种方式是实现Runnable接口,并将该接口实现类的实例作为参数传入Thread类的构造方法中。代码示例如下:

public class MyRunnable implements Runnable {

    @Override
    public void run() {
        // 这里是线程执行的逻辑
    }
}

// 将线程启动
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();

以上两种方式的区别是什么呢?对于第一种方式,我们需要重写Thread类中的run方法,在该方法中实现线程逻辑;而对于第二种方式,我们需要将Runnable接口实现类的实例传入Thread类的构造方法中,在该接口的run方法中实现线程逻辑。从某种程度上来说,第二种方式更加面向对象,因为它将线程逻辑从Thread类中抽离出来,更加符合面向对象的设计原则。

2. 线程的基本操作

Java中,线程作为程序中的执行单元,具有很多的基本操作,比如线程的启动、挂起、唤醒和终止等。

2.1 线程的启动

对于继承了Thread类的自定义线程,我们调用start方法即可启动一个新的线程。而对于实现了Runnable接口的自定义线程,我们需要将其作为参数传入Thread构造方法中实例化一个新线程,然后调用其start方法启动线程。

2.2 线程的挂起和唤醒

线程的挂起和唤醒可以使用suspend和resume方法来实现,但这些方法已经被废弃了,因为它们容易造成线程死锁。现在使用wait和notify方法来实现线程的挂起和唤醒。示例如下:

class MyThread extends Thread {
    private boolean stop = false;

    // 线程运行入口
    public void run() {
        while(!stop) {
            try {
                // 线程挂起
                synchronized(this) {
                    wait();
                }
            } catch (InterruptedException e) {

            }
        }
    }

    public void stopThread() {
        stop = true;
    }

    public void continueThread() {
        synchronized(this) {
            notifyAll();
        }
    }
}

2.3 线程的终止

线程的终止可以使用stop方法来实现,但是它已经被废弃了,因为它可能会造成线程死锁。现在,我们可以在run方法中使用一个boolean类型的变量来控制线程的终止。我们在线程启动前设置该变量为false,在需要结束线程时,将其设置为true。

3. 使用Java Concurrent包

Java中提供了Concurrent包,专门用于处理多线程的问题。使用该包可实现更高效的线程并发操作。本部分将介绍Java Concurrent包中常用的类以及其使用方法。

3.1 Lock锁

在Java Concurrent包中,提供了Lock接口,实现类有ReentrantLock和ReentrantReadWriteLock。Lock锁相较于synchronized,具有更细粒度的控制能力、更好的并发性、更好的可取消性以及更灵活的顺序规定等优点。

使用Lock锁的代码如下:

Lock lock = new ReentrantLock();
lock.lock();
try {
    // 线程安全操作
} finally {
    lock.unlock();
}

使用Lock锁时,需要手动获取锁和释放锁,原则上应在finally块中执行解锁操作。

3.2 Condition条件

Condition表示某个线程持有的锁上的等待区,通过await和signal方法来实现线程挂起和唤醒。示例代码如下:

Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();

// 线程等待
try {
    lock.lock();
    condition.await();
} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    lock.unlock();
}

// 线程唤醒
try {
    lock.lock();
    condition.signalAll();
} finally {
    lock.unlock();
}

3.3 AtomicInteger

AtomicInteger是一个支持原子操作的类,它可以在并发环境中实现线程安全性。示例代码如下:

AtomicInteger ai = new AtomicInteger(0);
ai.incrementAndGet();

3.4 CountDownLatch

CountDownLatch用于控制线程等待,让某些线程先执行,示例代码如下:

CountDownLatch countDownLatch = new CountDownLatch(5);
for(int i=0; i<5; i++) {
    new Thread(() -> {
        // 线程执行的逻辑
        countDownLatch.countDown(); // 线程执行完成
    }).start();
}
countDownLatch.await();  // 等待所有线程执行完成

4. 线程安全

在多线程编程中,当多个线程同时访问一个共享资源时,容易造成线程安全问题。线程安全问题包括以下几种:

  1. 临界区问题:多个线程同时访问一个共享资源,并对其进行修改,导致变量不一致的情况。
  2. 死锁问题:多个线程互相等待对方释放资源的情况。
  3. 非线程安全对象:某些对象在多线程环境下不能确保线程安全。

针对这些问题,我们可以使用锁、原子类、并发容器等方式来解决。

5. 示例说明

下面通过两个具体的示例,分别说明如何使用多线程实现异步执行操作。

5.1 示例一:查询多个网站的响应时间

该示例使用Java中的HttpURLConnection类来发起多个Web请求并获取其响应时间,可以用于查询多个网站的性能。

首先,我们定义一个线程类WebCheckThread,重写run方法,执行Web请求并记录响应时间。

public class WebCheckThread extends Thread {
    private String url;
    private List<Long> responseTime;

    public WebCheckThread(String url, List<Long> responseTime) {
        this.url = url;
        this.responseTime = responseTime;
    }

    @Override
    public void run() {
        long startTime = System.currentTimeMillis();
        HttpURLConnection connection = null;
        try {
            URL url = new URL(this.url);
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("HEAD");
            int responseCode = connection.getResponseCode();
            long endTime = System.currentTimeMillis();
            long time = endTime - startTime;
            responseTime.add(time);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(connection != null) {
                connection.disconnect();
            }
        }
    }
}

接着,在Main方法中,我们实例化多个WebCheckThread,并启动它们:

public static void main(String[] args) {
    String[] urls = new String[] {"http://www.baidu.com", "http://www.taobao.com", "http://www.jd.com", "http://www.am
    azon.com"};
    List<Long> responseTime = new ArrayList<>();
    for(String url : urls) {
        Thread thread = new WebCheckThread(url, responseTime);
        thread.start();
    }

    // 等待所有线程执行完成
    while(Thread.activeCount() > 2) {
        Thread.yield(); 
    }

    // 打印响应时间
    for(int i=0; i<urls.length; i++) {
        System.out.println(urls[i] + " - " + responseTime.get(i) + "ms");
    }
}

在上面的代码中,我们使用了while循环和Thread.yield()方法来等待所有线程执行完成。如果我们直接使用Thread.join()方法等待线程完成,则需要将线程的引用保存到一个线程数组中,这样代码会更加复杂。

5.2 示例二:使用线程池异步执行任务

该示例使用Java中的线程池来异步执行任务,提高任务执行效率。

首先,我们定义一个任务类Task,实现Runnable接口:

public class Task implements Runnable {
    private int taskId;

    public Task(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public void run() {
        try {
            System.out.println("Task " + taskId + " is running.");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

接着,在Main方法中,我们实例化ThreadPoolExecutor,并提交Task任务:

public static void main(String[] args) {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100));

    for(int i=0; i<20; i++) {
        Task task = new Task(i);
        executor.submit(task);
    }

    executor.shutdown();
}

在上面的代码中,我们使用ThreadPoolExecutor来创建线程池,并提交了20个Task任务,最后调用shutdown方法关闭线程池。

总结

本篇文章对Java创建多线程异步执行实现代码作出了详细的解释,主要包含了创建多线程的方式、线程的基础操作、Java Concurrent包以及线程安全的问题等方面的内容。通过两个示例,我们了解了如何使用多线程实现异步执行操作,包括查询多个网站的响应时间和使用线程池异步执行任务。希望本文能对读者在日常开发中解决多线程问题有所帮助。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java创建多线程异步执行实现代码解析 - Python技术站

(0)
上一篇 2023年5月17日
下一篇 2023年5月17日

相关文章

  • Springboot线程池并发处理数据优化方式

    下面我来详细讲解Spring Boot线程池并发处理数据优化方式的完整攻略。 1. 什么是线程池? 线程池指的是管理一组共享的线程资源,用于执行多个并发任务。线程池的原理是:在程序启动时,线程池会初始化一些线程,然后在线程池中等待任务的到来,这样就能够避免线程的频繁创建和销毁,从而提高程序的性能。 2. 为什么要使用线程池? 在Java程序中,如果每次需要处…

    多线程 2023年5月16日
    00
  • C#中的多线程多参数传递详解

    我们来详细讲解C#中的多线程多参数传递问题。 一、使用委托来传递多个参数 在C#中,我们可以使用委托来传递多个参数。具体步骤如下: 定义委托类型,包含所有需要传递的参数 public delegate void MyDelegate(string str1, int num1); 定义主函数,作为委托的执行体 public static void MyFun…

    多线程 2023年5月17日
    00
  • python 实现线程之间的通信示例

    当我们在使用多线程的时候,往往需要让多线程之间进行通信,共享数据或资源,而 Python 提供了多种方式来实现线程之间的通信,本文将进行详细讲解。 一、Python 实现线程之间的通信 Python 提供了多种方式来实现线程之间的通信,主要包括: 库模块: threading 模块提供了 Lock、RLock、Condition、Semaphore 等多种同…

    多线程 2023年5月17日
    00
  • Mysql事务并发问题解决方案

    那我来详细讲解一下 MySQL 事务并发问题的解决方案。 什么是 MySQL 事务并发问题 并发问题指多个用户同时访问同一份数据时,由于读写操作的顺序不同,产生了冲突,导致数据出现异常。MySQL 数据库在支持事务的同时,也存在并发问题。 比如,用户 A 和用户 B 同时对一个数据进行操作,A 想要写入数据,B 想要读取数据。若此时 B 先读取了数据,但 A…

    多线程 2023年5月16日
    00
  • SpringBoot 并发登录人数控制的实现方法

    下面我来为你详细讲解“SpringBoot 并发登录人数控制的实现方法”的完整攻略。 1. 前言 在实际开发过程中,我们经常需要加入并发登录人数控制的功能。SpringBoot 作为目前最流行的 JavaWeb 框架之一,其内置的 Spring Security 在实现登录控制方面有很大的优势。同时,SpringBoot 还提供了一些自定义实现方式,用于满足…

    多线程 2023年5月16日
    00
  • 详解Java并发之Condition

    详解Java并发之Condition Condition是什么? Condition是Java并发包中的一个接口,它是对传统Object.wait()和Object.notify()方法的增强,可以更灵活地实现线程的等待和通知。 创建一个Condition对象 创建Condition对象通常是在Lock对象的基础上创建的,可以通过Lock接口的newCond…

    多线程 2023年5月16日
    00
  • 详解MySQL多版本并发控制机制(MVCC)源码

    详解MySQL多版本并发控制机制(MVCC)源码 一、MVCC简介 MVCC(Multi-Version Concurrency Control)即多版本并发控制,是MySQL的一种高性能的事务处理方式。 MVCC基于快照的概念,即每个事务在执行时都会在内部生成一份数据快照,用于记录当前时刻的数据状态。当有其他事务需要读取数据时,它们实际上访问的是已经生成的…

    多线程 2023年5月17日
    00
  • Java并发计数器的深入理解

    Java并发计数器的深入理解 什么是Java并发计数器 Java并发计数器是一项重要的多线程技术,它可以在多线程环境下高效地实现数据的计数。 Java并发计数器的本质是使用CAS原子操作实现的,CAS的全称是Compare and Swap,即“比较并交换”,CAS提供了一种无锁化的解决方案,让多线程同时更新同一个数据变得更加高效。 实现原理 在并发计数器的…

    多线程 2023年5月16日
    00
合作推广
合作推广
分享本页
返回顶部