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. 线程安全
在多线程编程中,当多个线程同时访问一个共享资源时,容易造成线程安全问题。线程安全问题包括以下几种:
- 临界区问题:多个线程同时访问一个共享资源,并对其进行修改,导致变量不一致的情况。
- 死锁问题:多个线程互相等待对方释放资源的情况。
- 非线程安全对象:某些对象在多线程环境下不能确保线程安全。
针对这些问题,我们可以使用锁、原子类、并发容器等方式来解决。
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技术站