Java多线程编程高级应用
Java多线程编程是Java中的核心技术之一,它可以帮助我们在程序中同时执行多个任务,提高程序的执行效率。然而,理解和正确使用Java的多线程编程并不是一件简单的事情,需要对其底层的工作原理有深入的了解。
下面,我们将介绍Java多线程编程的高级应用,包括线程池、同步、锁定、并发集合、线程的调度策略等内容,并且附上相关的代码示例。
1. 线程池
线程池是一种基于池化思想管理线程的工具,可以控制系统中运行的线程数量,根据系统的性能动态地调整线程数量。
1.1 创建线程池
Java的java.util.concurrent.Executors
类提供了创建不同类型线程池的工厂方法。以下是创建一个固定大小的线程池的例子:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolExample {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread("" + i);
executorService.execute(worker);
}
executorService.shutdown();
while (!executorService.isTerminated()) {
}
System.out.println("Finished all threads");
}
}
class WorkerThread implements Runnable {
private String command;
public WorkerThread(String s) {
this.command = s;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " Start. Command = " + command);
processCommand();
System.out.println(Thread.currentThread().getName() + " End.");
}
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在这段代码中,我们创建了一个固定大小(5个线程)的线程池,并向里面提交了10个任务。每个任务就是一个实现了Runnable接口的WorkerThread对象。任务一旦提交,就会由线程池中的一个空闲线程来执行。如果线程池中没有空闲的线程,新提交的任务就会被放在等待队列中,等待有线程空闲时再执行。
1.2 线程池的关闭
当我们不再需要线程池时,应该关闭它,以释放资源。可以通过调用ExecutorService
的shutdown()
方法来关闭线程池。这个方法会等待所有已提交的任务都完成后,再关闭线程池。
2. 同步
Java的synchronized
关键字可以用来确保多个线程在访问共享资源时,不会同时进行,从而避免数据不一致的问题。
下面的例子展示了如何使用synchronized
关键字来保证数据的一致性:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized void decrement() {
count--;
}
public synchronized int value() {
return count;
}
}
在这段代码中,我们定义了一个Counter类,它有一个int类型的成员变量count,以及三个方法:increment()、decrement()和value()。这三个方法都被声明为synchronized,这意味着在同一时间,只有一个线程能够执行这些方法。
3. 锁定
Java的java.util.concurrent.locks
包提供了一些更灵活的锁定机制,比如ReentrantLock
。
下面的例子展示了如何使用ReentrantLock
:
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
private int count = 0;
// Locking using Lock and ReentrantLock
publicint getCount() {
lock.lock();
try {
System.out.println("Lock acquired by " + Thread.currentThread().getName());
return count++;
} finally {
lock.unlock();
System.out.println("Lock released by " + Thread.currentThread().getName());
}
}
public static void main(String[] args) {
final ReentrantLockExample counter = new ReentrantLockExample();
Runnable r = () -> {
for (int i = 0; i < 3; i++) {
System.out.println(Thread.currentThread().getName() + " gets count: " + counter.getCount());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(r, "Thread 1").start();
new Thread(r, "Thread 2").start();
}
}
在这段代码中,我们定义了一个ReentrantLockExample
类,它有一个ReentrantLock
类型的成员变量lock和一个int类型的成员变量count。getCount()方法首先获得锁,然后打印一条消息,增加count的值,再解锁并打印一条消息。
4. 并发集合
Java的java.util.concurrent
包中还提供了许多并发集合,比如ConcurrentHashMap
、CopyOnWriteArrayList
等。这些集合在支持高并发的同时,还能保证数据的一致性。
下面的例子展示了如何使用ConcurrentHashMap
:
import java.util.concurrent.ConcurrentHashMap;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
ConcurrentHashMap<String, String> map = new ConcurrentHashMap<String, String>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.remove("key1");
}).start();
new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.remove("key2");
}).start();
for (Object key : map.keySet()) {
System.out.println(key + " : " + map.get(key));
}
}
}
在这段代码中,我们首先创建了一个ConcurrentHashMap
对象,并向其中添加了三个元素。然后我们启动了两个线程,每个线程都在1秒后删除一个元素。最后,我们遍历并打印出map中的所有元素。
5. 线程的调度策略
Java的线程调度策略基于优先级。可以通过Thread类的setPriority()方法来设置线程的优先级。优先级高的线程有更大的几率被操作系统选中执行。
下面的例子展示了如何设置线程的优先级:
public class PriorityExample {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
while (true) {
System.out.println("Thread 1");
}
});
t1.setPriority(1);
Thread t2 = new Thread(() -> {
while (true) {
System.out.println("Thread 2");
}
});
t2.setPriority(10);
t1.start();
t2.start();
}
}
在这段代码中,我们创建了两个线程,一个的优先级设置为1(最低),另一个的优先级设置为10(最高)。然后同时启动这两个线程。由于t2的优先级更高,因此它有更大的几率被操作系统选中执行。
在Java多线程编程中,理解线程的生命周期、线程同步、线程通信以及线程的调度策略等是非常重要的。通过这些高级应用,我们可以更好地控制程序的并发行为,提高程序运行的效率,同时也可以避免程序的错误和异常。