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

巧妙运用Java中的多线程函数优化程序性能

发布时间:2023-06-21 03:05:30

随着计算机硬件性能不断提升,软件应用程序的性能也成为了越来越重要的问题。其中,多线程是提升程序性能的一种重要方法。Java作为一门多线程支持良好的语言,在实际开发中很多场景都可以通过多线程优化程序性能。本文将就如何巧妙运用Java中的多线程函数优化程序性能进行讨论。

# 1. 多线程的基本操作

Java中多线程的基本操作主要涉及Thread类和Runnable接口。创建线程的方法有两种,一种是继承Thread类,一种是实现Runnable接口。这里我们以实现Runnable接口为例,来看一个简单的示例程序:

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("当前线程:" + Thread.currentThread().getName());
    }

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

该程序创建了一个实现了Runnable接口的MyRunnable类,并重写了其run方法,用于打印当前线程的名称。在main方法中,创建了两个Thread对象,分别传入MyRunnable实例,并调用start方法,启动了两个线程。

运行程序可以看到,打印的结果是两个线程轮流打印的,说明程序中的两个线程是并发执行的:

当前线程:Thread-0
当前线程:Thread-1

# 2. 线程池的使用

在实际开发中,很多场景都需要大量地创建线程来完成任务,这时候使用线程池可以提高程序的性能。线程池可以有效地复用线程,减少线程的创建和销毁的开销,从而提高程序的运行效率。

在Java中,线程池的使用也非常简单,只需要通过Executors类创建一个线程池对象即可。下面是一个简单的示例程序:

public class ThreadPoolDemo {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
        for (int i = 1; i <= 10; i++) {
            executor.execute(new WorkerThread("Thread " + i));
        }
        executor.shutdown();
        while (!executor.isTerminated()) {

        }
        System.out.println("线程池已结束");
    }
}

class WorkerThread implements Runnable {
    private String name;

    public WorkerThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(name + " 正在运行");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + " 运行结束");
    }
}

该程序创建了一个固定大小为3的线程池对象executor,并通过for循环创建了10个WorkerThread对象,将其作为参数传入executor的execute方法中。其中WorkerThread是一个简单的实现Runnable接口的类,用于打印线程的名称,并模拟线程的运行。打印的结果如下:

Thread 1 正在运行
Thread 2 正在运行
Thread 3 正在运行
Thread 1 运行结束
Thread 4 正在运行
Thread 5 正在运行
Thread 2 运行结束
Thread 6 正在运行
Thread 7 正在运行
Thread 8 正在运行
Thread 5 运行结束
Thread 4 运行结束
Thread 9 正在运行
Thread 10 正在运行
Thread 6 运行结束
Thread 7 运行结束
Thread 3 运行结束
Thread 8 运行结束
Thread 9 运行结束
Thread 10 运行结束
线程池已结束

# 3. 多线程应用场景

多线程在实际开发中有很多应用场景,下面将介绍其中的一些:

## 3.1 多线程爬虫

爬虫是一种常见的应用场景,为了提高爬取速度,可以采用多线程的方式进行爬取。例如下面的示例程序,采用了4个线程进行爬取:

public class SpiderDemo {
    private static int threadCount = 4;
    private static String url = "https://www.baidu.com";
    private static Pattern pattern = Pattern.compile("href=\"(https?://[^\"]*)\"");

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
        List<Future<List<String>>> resultList = new ArrayList<>();
        resultList.add(executorService.submit(new Spider(url)));
        for (int i = 0; i < resultList.size(); i++) {
            Future<List<String>> future = resultList.get(i);
            try {
                List<String> urls = future.get();
                for (String url : urls) {
                    resultList.add(executorService.submit(new Spider(url)));
                }
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }
        executorService.shutdown();
    }

    static class Spider implements Callable<List<String>> {
        private String url;

        Spider(String url) {
            this.url = url;
        }

        @Override
        public List<String> call() throws Exception {
            System.out.println(Thread.currentThread().getName() + " 正在爬取 " + url);
            List<String> urls = new ArrayList<>();
            URLConnection connection = new URL(url).openConnection();
            connection.setConnectTimeout(1000);
            connection.setReadTimeout(1000);
            connection.connect();
            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
            String line;
            while ((line = reader.readLine()) != null) {
                Matcher matcher = pattern.matcher(line);
                while (matcher.find()) {
                    urls.add(matcher.group(1));
                }
            }
            reader.close();
            System.out.println(Thread.currentThread().getName() + " 爬取完成 " + url);
            return urls;
        }
    }
}

该程序在主线程中先创建了一个固定大小的线程池executorService,并通过submit方法将一个Spider对象加入到线程池中。Spider类实现了Callable接口,用于爬取给定url页面的超链接。在主线程中,通过resultList不断获取线程的返回结果,从而进行下一轮的爬取。该程序输出的爬取结果如下:

`

pool-1-thread-1 正在爬取 https://www.baidu.com

pool-1-thread-2 爬取完成 https://www.baidu.com

pool-1-thread-2 正在爬取 https://news.baidu.com

pool-1-thread-3 正在爬取 https://www.baidu.com/s?tn=02003390_2_hao_pg&bs=Java

pool-1-thread-4 爬取完成 https://www.baidu.com/s?tn=02003390_2_hao_pg&bs=Java

pool-1-thread-4 正在爬取 https://www.java.com

pool-1-thread-5 正在爬取 https://news.baidu.com/?tn=news

pool-1-thread-6 爬取完成 https://news.baidu.com/

pool-1-thread-6 正在爬取 https://mil.baidu.com

pool-1-thread-7 正在爬取 https://home.baidu.com/index/

pool-1-thread-1 爬取完成 https://www.baidu.com

pool-1-thread-1 正在爬取 https://tieba.baidu.com/index.html

pool-1-thread-8 正在爬取 https://fanyi.baidu.com

pool-1-thread-3 爬取完成 https://www.baidu.com/s?tn=02003390_2_hao_pg&bs=Java

pool-1-thread-9 正在爬取 https://www.baidu.com/jr/jingrong.html

pool-1-thread-5 爬取完成 https://news.baidu.com/?tn=news

pool-1-thread-5 正在爬取 https://www.hao123.com?tn=96842748_h