实现多线程的三种方法
继承Thread类
通过继承Thread来实现多线程
Java是通过java.lang.Thread 类来代表线程的。
按照面向对象的思想,Thread类应该提供了实现多线程的方式。
多线程的实现方案一:继承Thread类
第一步:定义一个子类MyThread继承线程类java.lang.Thread,重写run()方法
第二步:创建MyThread类的对象
第三步:调用线程对象的start()方法启动线程(启动后还是执行run方法的)
/**
* 本项目主要记录了多线程的三种实现方式
*/
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
MyThread myThread = new MyThread();
myThread.start();
for (int i = 0; i < 50; i++) {
System.out.println("main线程"+i);
}
}
}
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("子线程"+i);
}
}
}
方式一优缺点:
优点:编码简单
缺点:线程类已经继承Thread,无法继承其他类,不利于扩展。
实现Runnable接口
1.定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法
2.创建MyRunnable任务对象
3.把MyRunnable任务对象交给Thread处理。
4.调用线程对象的start()方法启动线程
public class Demo4 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
for (int i = 0; i < 50; i++) {
System.out.println("主线程"+i);
}
}
}
class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("子线程"+i);
}
}
}
Thread的构造器
public Thread(String name) 可以为当前线程指定名称
public Thread(Runnable target) 封装Runnable对象成为线程对象
public Thread(Runnable target ,String name )
封装Runnable对象成为线程对象,并指定线程名称
所以我们创建线程对象的时候可以包装Runnable对象从而实现多线程。
方式二优缺点:
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
缺点:编程多一层对象包装,如果线程有执行结果是不可以直接返回的。
利用Callable、FutureTask接口实现。
1.得到任务对象
定义类实现Callable接口,重写call方法,封装要做的事情。
用FutureTask把Callable对象封装成线程任务对象。
2.把线程任务对象交给Thread处理。
3.调用Thread的start方法启动线程,执行任务
4.线程执行完毕后、通过FutureTask的get方法去获取任务执行的结果。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class Demo5 {
public static void main(String[] args) {
Callable<String> myCallable = new myCallable();
FutureTask<String> f1=new FutureTask<>(myCallable);
Thread thread = new Thread(f1);
thread.start();
String s = null;
for (int i = 0; i < 50; i++) {
System.out.println("主线程"+i);
}
try {
s = f1.get();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(s);
}
}
class myCallable implements Callable<String>{
@Override
public String call() throws Exception {
for (int i = 0; i < 50; i++) {
System.out.println("子线程"+i);
}
return "zmm";
}
}
方式三优缺点:
优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强。
可以在线程执行完毕后去获取线程执行的结果。
缺点:编码复杂一点。
线程池
线程池就是用来解决所谓的来一个任务就创建一个新线程,而线程创建与回收都是比较耗费资源的。如果遭受到恶意攻击,可能会很快瘫痪。
线程池就是线程复用技术。通过一个任务队列来缓存一些任务,等其他任务完成再来进行处理。
如何得到线程池对象
JDK 5.0起提供了代表线程池的接口:ExecutorService
方式一:使用ExecutorService的实现类ThreadPoolExecutor自创建一个线程池对象
ThreadPoolExecutor构造器的参数说明
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
参数一:核心线程数量。
参数二:最大线程数量
参数三:临时线程存活时间
参数四:临时线程存活时间的单位
参数五:任务队列,
参数六:线程工厂,指定用哪个线程工厂创建线程: threadFactory
参数七:指定线程忙,任务满的时候,新任务来了怎么办:
Q临时线程什么时候创建啊?
A:当前线程池所有核心线程都在忙,且任务队列满的时候,会创建临时线程。
Q什么时候会开始拒绝任务?
A:当前线程池所有的核心线程和临时线程都在忙,并且任务队列满的时候,才会启用拒绝策略。
线程池处理Runnable任务
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import static java.util.concurrent.TimeUnit.SECONDS;
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
ExecutorService pool =new ThreadPoolExecutor(3,5,2,SECONDS,
new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
//给任务让线程池处理
Runnable myThread = new MyThread();
pool.execute(myThread);
pool.execute(myThread);
pool.execute(myThread);
/*
pool.shutdownNow();//立即关闭,即使任务没完成。
pool.shutdown();//等待任务处理完毕
*/
}
}
Runnable的实现类。
public class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
ExecutorService接口的常用方法
void execute(Runnable command) 用来执行Runnable任务。
Future submit(Callable task)
执行任务,返回未来任务对象获取线程结果,一般拿来执行 Callable 任务
void shutdown() 关闭线程池,但是会等待任务执行完成。
List shutdownNow() 关闭线程池,不会等待任务执行完成。放回任务队列,表示未完成的任务。
线程池执行Callable任务
import java.util.concurrent.*;
import static java.util.concurrent.TimeUnit.SECONDS;
public class Main1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println("Hello world!");
ExecutorService pool =new ThreadPoolExecutor(3,5,2,SECONDS,
new ArrayBlockingQueue<>(5), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
//给任务让线程池处理
/*Callable<String> task1 = new Mycallable(100);*/
Future<String> submit = pool.submit(new Mycallable(100));
System.out.println(submit.get());
System.out.println(pool.submit(new Mycallable(200)).get());
System.out.println(pool.submit(new Mycallable(300)).get());
System.out.println(pool.submit(new Mycallable(400)).get());
System.out.println(pool.submit(new Mycallable(500)).get());
}
}
实现Callable接口
import java.util.concurrent.Callable;
public class Mycallable implements Callable<String> {
private int n;
public Mycallable(int n) {
this.n = n;
}
@Override
public String call() throws Exception {
int sum=0;
for (int i = 1; i <= n; i++) {
sum+=i;
}
return Thread.currentThread().getName()+",1-"+n+"的和为"+sum;
}
}
Executors得到线程池对象的常用方法
public static ExecutorService newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
看这个源码可以知道底层也是调用ThreadPoolExecutor来创建线程池。
new CachedThreadPool corePoolsize为0.就是没有核心线程。最大线程数为Integer.MAX_VALUES
所以线程数量会随着任务增加而增加。创建的线程数量最大上限是Integer.MAX_VALUE,
线程数可能会随着任务1:1增长,也可能出现OOM错误( java.lang.OutOfMemoryError )
public static ExecutorService newSingleThreadExecutor()
new SingleThreadExecutor
核心线程和最大线程为1.创建只有一个线程的线程池对象,如果该线程出现异常而结束,那么线程池会补充一个新线程。允许请求的任务队列长度是Integer.MAX_VALUE,可能出现OOM错误( java.lang.OutOfMemoryError )
public static ExecutorService newFixedThreadPool(int nThreads)
new FixedThreadPool创建一个固定线程的线程池。如果某个线程因为执行异常而结束,那么线程池会补充一个新线程替代它。允许请求的任务队列长度是Integer.MAX_VALUE,可能出现OOM错误( java.lang.OutOfMemoryError )
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建一个线程池,可以实现在给定的延迟后运行任务,或者定期执行任务。 创建的线程数量最大上限是Integer.MAX_VALUE,
线程数可能会随着任务1:1增长,也可能出现OOM错误( java.lang.OutOfMemoryError )