java多线程三种实现方式以及线程池

发布于:2022-12-07 ⋅ 阅读:(505) ⋅ 点赞:(0)

实现多线程的三种方法

继承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 )

本文含有隐藏内容,请 开通VIP 后查看