多线程的几种实现方式
线程创建与运行
Java 中有三种线程创建方法,分别为实现 Runnable 接口的run方法、继承 Thread 类并重写 run 方法、使用 FutureTask 方式。
首先看下继承 Thread 方法的实现:
public class ThreadTest {
//继承Thread类并重写run方法
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println("I am a child thread");
}
}
public static void main(String[] args) {
// 创建线程
MyThread thread = new MyThread();
// 启动线程
thread.start();
}
}
如上代码 MyThread 类继承了 Thread 类,并重写了 run 方法,然后调用了线程的 start 方法启动了线程,当创建完 thread 对象后该线程并没有被启动执行.
当调用了 start 方法后才是真正启动了线程。其实当调用了 start 方法后线程并没有马上执行而是处于就绪状态,这个就绪状态是指该线程已经获取了除 CPU 资源外的其它资源,等获取 CPU 资源后才会真正处于运行状态。
当 run 方法执行完毕,该线程就处于终止状态了。使用继承方式好处是 run 方法内获取当前线程直接使用 this 就可以,无须使用 Thread.currentThread() 方法,不好的地方是 Java 不支持多继承,如果继承了 Thread 类那么就不能再继承其它类,另外任务与代码没有分离,当多个线程执行一样的任务时候需要多份任务代码,而 Runable 则没有这个限制,下面看下实现 Runnable 接口的 run 方法方式:
public static class RunableTask implements Runnable{
@Override
public void run() {
System.out.println("I am a child thread");
}
}
public static void main(String[] args) throws InterruptedException{
RunableTask task = new RunableTask();
new Thread(task).start();
new Thread(task).start();
}
如上面代码,两个线程公用一个 task 代码逻辑,需要的话 RunableTask 可以添加参数进行任务区分,另外 RunableTask 可以继承其他类,但是上面两种方法都有一个缺点就是任务没有返回值,下面看最后一种是使用 FutureTask:
//创任务类,类似Runable
public static class CallerTask implements Callable<String>{
@Override
public String call() throws Exception {
return "hello";
}
}
public static void main(String[] args) throws InterruptedException {
// 创建异步任务
FutureTask<String> futureTask = new FutureTask<>(new CallerTask());
//启动线程
new Thread(futureTask).start();
try {
//等待任务执行完毕,并返回结果
String result = futureTask.get();
System.out.println(result);
} catch (ExecutionException e) {
e.printStackTrace();
}
}
注:每种方式都有自己的优缺点,应该根据实际场景进行选择。
更多内容详见https://gitchat.csdn.net/activity/5aa4d205c2ff6f2e120891dd?utm_source=so
多线程的作用
1.可以解决负载均衡问题,充分利用CPU的资源,为了提高Cpu的使用,采用多线程的方法去同时完成几件事情而互不干扰
2.大多数的情况下, 使用多线程 主要是需要处理大量的IO操作或处理的情况需要花大量的时间等
3.异步处理,将程序中占据时间长的任务放到后台去处理
好处:
1、使用线程可以把程序中占据时间长的任务放到后台去处理,如图片、视频的下载
2、发挥多核处理器的优势,并发执行让系统运行的更快、更流畅,用户体验更好
缺点:
1、大量的线程降低代码的可读性,
2、更多的线程需要更多的内存空间
3、当多个线程对同一个资源出现争夺的时候要注意线程安全的问题。
实例
1.异步处理
不使用线程:
package thread;
public class SleepTest {
public static void main(String[] args) throws Exception {
System.out.println("begin");
Thread.sleep(5000);//模拟业务场景
System.out.println("end");
}
}
例子很简单,控制台输出begin并且在五秒后打印end
使用线程
package thread;
public class SleepTest {
public static void main(String[] args) throws Exception {
System.out.println("begin");
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);//模拟业务场景
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
System.out.println("end");
}
}
效果,执行后控制台立刻显示begin和end
因为目前我们不需要中间处理的结果,因此可以让它在后台执行,不阻塞主线程。
另外,也可以使用线程池实现上面的功能,代码如下:
package thread;
import java.util.concurrent.Executors;
public class SleepTest {
public static void main(String[] args) throws Exception {
System.out.println("begin");
Executors.newSingleThreadExecutor().submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);// 模拟业务场景
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
System.out.println("end");
}
}
2.文件转pdf
package org.framework.core.extend.swftools;
import org.framework.core.util.FileUtils;
/**
* 文件转换调用接口
*
* @author mosesframe
*
*/
public class SwfToolsUtil {
public static void convert2SWF(final String inputFile) {
new Thread("文件转化服务") {
public void run() {
String extend = FileUtils.getExtend(inputFile);
PDFConverter pdfConverter = new OpenOfficePDFConverter();
SWFConverter swfConverter = new SWFToolsSWFConverter();
if (extend.equals("pdf")) {
// swfConverter.convert2SWF(inputFile,extend);
// 该项目这里注释掉,前台预览使用了pdf.js,因此不需要转换成swf文件了,避免浪费更多的时间
}
if (extend.equals("doc") || extend.equals("docx")
|| extend.equals("xls") || extend.equals("pptx")
|| extend.equals("xlsx") || extend.equals("ppt")
|| extend.equals("txt") || extend.equals("odt")) {
DocConverter converter = new DocConverter(pdfConverter,
swfConverter);
converter.convert(inputFile, extend);
}
}
}.start();
}
}