随着互联网技术的迅速发展,高并发编程成为现代软件开发中不可忽略的一个问题。在这个过程中,Java 作为一门流行的编程语言,具有很高的性能和可靠性,因此 Java 在高并发编程中也有它很好的表现。
线程和进程
在进入高并发编程之前,我们需要先理解线程和进程。进程是一个运行中的程序,它拥有独立的内存空间和系统资源,可以看作一个独立的工作单元。线程是一个独立的执行流,它是进程中的一个实体,负责执行进程中的任务。一个进程可以有多个线程。
Java 中的线程是通过 Thread 类来实现的。创建线程的方式有两种,一种是继承 Thread 类,重写其 run() 方法,如下所示:
// javascriptcn.com 代码示例 public class MyThread extends Thread { @Override public void run() { // 线程执行的代码 } } MyThread myThread = new MyThread(); myThread.start(); // 开始执行线程
另一种是实现 Runnable 接口,实现其 run() 方法,然后将其传给 Thread 类,如下所示:
// javascriptcn.com 代码示例 public class MyRunnable implements Runnable { @Override public void run() { // 线程执行的代码 } } MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); // 开始执行线程
高性能并发编程的常用工具
Java 中有很多常用的工具可以帮助我们实现高性能并发编程。下面介绍一些常用的工具。
锁
锁是一种用于保护共享资源的机制,Java 中提供了多种类型的锁,如下所示:
- synchronized:Java 中最基本的锁,在方法或代码块上使用 synchronized 关键字可以保证同步访问某一共享资源。
- ReentrantLock:Java 中提供的可重入锁,可多次获取同一把锁。
- ReadWriteLock:Java 中提供的读写锁,允许多个线程同时读一个共享资源,但只允许一个线程写共享资源,写锁是具有排他性的。
线程池
线程池管理着一个线程队列,通过重用线程来减少线程创建和销毁的开销,从而提高系统的性能和响应速度。Java 中提供了 Executor 框架和 ThreadPoolExecutor 类来实现线程池。
并发队列
并发队列是线程安全的队列,Java 中提供了多种并发队列,如下所示:
- ArrayBlockingQueue:基于数组的有界阻塞队列。
- LinkedBlockingQueue:基于链表的无界阻塞队列。
- SynchronousQueue:一种没有缓冲的队列,在这种队列中每个插入操作必须等待另一个线程的移除操作,反之亦然。
原子变量
原子变量是线程安全的变量,Java 中提供了多种原子变量类型,如 AtomicInteger、AtomicBoolean 等。原子变量的操作是具有原子性和可见性的,其操作不会被中断,保证了线程安全。
实例分析
下面以多线程统计单词个数的实现为例,介绍 Java 高性能并发编程的常用工具的应用。
线程池的使用
在单机环境中,实现多线程任务的常用方法是手动创建线程并执行。但是,线程的创建和销毁开销很大,而且过多的线程会使系统资源消耗更多,降低整体性能。因此,我们可以使用线程池来管理线程,从而减少线程的创建和销毁开销。
// javascriptcn.com 代码示例 public class WordCount { ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); // 创建线程池,大小为 CPU 核心数的两倍 Map<String, Integer> result = new ConcurrentHashMap<>(); // 使用并发哈希表存储结果,保证线程安全 public Map<String, Integer> count(String[] files) { for (String file : files) { executorService.execute(new FileHandler(file, result)); // 将文件处理任务提交到线程池 } executorService.shutdown(); // 发出结束池的指令 try { executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); // 等待池内任务执行完成 } catch (InterruptedException e) { e.printStackTrace(); } return result; } }
并发队列的使用
在多线程操作共享数据时,容易出现线程安全问题,因此需要使用线程安全的并发队列。在单词统计任务中,我们可以使用阻塞队列来存储待处理的文件名。
// javascriptcn.com 代码示例 public class WordCount { BlockingQueue<String> queue = new LinkedBlockingQueue<>(); // 创建阻塞队列,存储待处理的文件名 Map<String, Integer> result = new ConcurrentHashMap<>(); // 使用并发哈希表存储结果,保证线程安全 public Map<String, Integer> count(String[] files) { for (String file : files) { queue.offer(file); // 将文件名放入阻塞队列 } ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); // 创建线程池,大小为 CPU 核心数的两倍 for (int i = 0; i < Runtime.getRuntime().availableProcessors() * 2; i++) { executorService.execute(new FileHandler(queue, result)); // 将文件处理任务提交到线程池 } executorService.shutdown(); // 发出结束池的指令 try { executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); // 等待池内任务执行完成 } catch (InterruptedException e) { e.printStackTrace(); } return result; } }
原子变量的使用
在多线程操作共享数据时,容易出现多个线程同时访问共享数据导致数据异常的问题。使用原子变量可以避免该问题。
// javascriptcn.com 代码示例 public class WordCount { BlockingQueue<String> queue = new LinkedBlockingQueue<>(); // 创建阻塞队列,存储待处理的文件名 AtomicInteger count = new AtomicInteger(0); // 使用 Atomic 变量存储处理过的文件数 Map<String, Integer> result = new ConcurrentHashMap<>(); // 使用并发哈希表存储结果,保证线程安全 public Map<String, Integer> count(String[] files) { for (String file : files) { queue.offer(file); // 将文件名放入阻塞队列 } ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2); // 创建线程池,大小为 CPU 核心数的两倍 for (int i = 0; i < Runtime.getRuntime().availableProcessors() * 2; i++) { executorService.execute(new FileHandler(queue, count, result)); // 将文件处理任务提交到线程池 } executorService.shutdown(); // 发出结束池的指令 try { executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); // 等待池内任务执行完成 } catch (InterruptedException e) { e.printStackTrace(); } return result; } }
总结
Java 中提供了很多高性能并发编程的工具和技术,如锁、线程池、并发队列和原子变量等。合理的使用这些工具和技术可以提高系统的性能和响应速度,同时保证多线程操作的安全性。在实际开发中,需要根据实际情况选择合适的工具和技术,并进行合理的使用和管理。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/653346087d4982a6eb6c7863