- 继承Thread类实现多线程
- 利用Runnable接口实现多线程
- Callable实现多线程
- 利用线程池来实现多线程
继承Thread类实现多线程
自定义的类继承Thread类后,覆写父类中的run方法,通过start方法来调用run方法启动线程
简单写下代码:
public class Main{
public static void main(String args[]) {
//创建了三个线程
myThread myThread1=new myThread("thread1");
myThread myThread2=new myThread("thread2");
myThread myThread3=new myThread("thread3");
//通过start方法来调用run方法
myThread1.start();
myThread2.start();
myThread3.start();
}
}
class myThread extends Thread{
private String name;
public myThread(String name){
this.name=name;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.name+" i="+i);
}
}
}
利用Runnable接口实现多线程
Runnable接口解决了单继承的问题
自定义一个类继承 Runnable 接口,实现 run 方法,通过 Thread 中的 public Thread ( Runnable target ) 方法来使用 start 方法启动线程,否则无法启动线程
注意:多线程的启动永远使用的是Thread的start方法
public class Main{
public static void main(String args[]) {
myThread myThread1=new myThread("thread1");
myThread myThread2=new myThread("thread2");
myThread myThread3=new myThread("thread3");
new Thread(myThread1).start();
new Thread(myThread2).start();
new Thread(myThread3).start();
}
}
class myThread implements Runnable{
private String name;
public myThread(String name){
this.name=name;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.name+" i="+i);
}
}
}
Callable实现多线程
先看Callable的继承树:
Callable接口和Thread类并不是直接有联系,所以使用Callable接口要借助FutureTask,FutureTask可以保证任务在多线程的情况下只被一个线程执行一次,
FutureTask中的get方法可以得到call方法的返回值,这也是FutureTask和以上两种方法的不同点,它具有返回值
简单看下代码:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Main{
public static void main(String args[]) throws ExecutionException, InterruptedException {
FutureTask<String> futuretask=new FutureTask<>(new myThread());
Thread thread1=new Thread(futuretask);
Thread thread2=new Thread(futuretask);
thread1.start();
thread2.start();
System.out.println(futuretask.get());
}
}
class myThread implements Callable<String> {
private int ticket=10;
@Override
public String call() throws Exception {
while (ticket>0){
System.out.println("剩余票数:"+ticket--);
}
return "票已售罄";
}
}
利用线程池来完成多线程
线程池的实现原理:
- 向线程池里提交了一个任务,首先判断线程池的数量是否已达到corePollSize,若没达到,则放入执行任务,若已达到,判断是否有空闲线程,若有,执行任务,若没有,进入下一步
- 判断当前线程池的数量有没有达到当前线程池的最大数量,若没有,则放入任务,否则进入下一步
- 判断阻塞队列是否已满,若未满,放入阻塞队列中等待调度,否则,进入下一步
- 调用相应的拒绝策略打回任务(有四种拒绝策略,默认扔出AbortPolicy异常)
内置的四大线程池:
- 固定大小线程池:FixedThreadPool,适用于负载较重的服务器,来满足资源分配的要求
- 单线程池:SingleThreadPoolExecutor,只有一个线程,在多线程场景下需要让任务串行执行
- 缓存线程池:CachedThreadPool,适用于负载较轻的服务器,或执行很多的短期异地任务
- 定时调度池:ScheduledThreadPoolExecutor,适用在给定的延迟之后运行任务,或者定期执行任务
创建线程池:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
向线程池提交任务有两种方法:
- execute() ,提交不需要返回值的任务,无法判断任务是否被线程池执行成功
- submit(),提交需要返回值的任务,线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值
关闭线程池有两种方法:
- shutdown,只是将线程池的状态设置成SHUTDOWN,然后中断所有没有正在执行任务的线程
- shutdownNow,将线程池的状态设置成了STOP,然后停止所有任务,并返回等待执行任务的列表
简单写下代码:
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class Main{
public static void main(String args[]) {
myThread mythread=new myThread();
ThreadPoolExecutor threadPoolExecutor=
new ThreadPoolExecutor
(3,5,2000,TimeUnit.MILLISECONDS,
new LinkedBlockingDeque<Runnable>());
for (int i = 0; i < 5; i++) {
threadPoolExecutor.execute(mythread);
}
threadPoolExecutor.shutdownNow();
}
}
class myThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
在实现多线程时,推荐使用线程池来实现
线程池的优点:
- 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁带来的消耗
- 提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立即执行
- 提高线程的可管理性:使用线程池可以统一进行线程分配、调度和监控