java 线程与线程池详解

并发:同一时刻,多任务交替执行,造成一种“貌似同时”的错觉。简言之,单核cpu实现的多任务就是并发。

并行:同一时刻,多任务同时执行。多核cpu可实现并行。

在创建线程时,可以使用线程池进行管理,也可以直接创建新线程。什么时候适合使用线程池呢?

当线程涉及到频繁的创建与销毁时,适合使用线程池;如果线程只涉及单纯的创建,并没有销毁时,直接创建既可(例如在创建长连接时,保持心跳的线程接收服务端消息推送的线程)。

一.线程

1.创建  通过继承Thread类来重写run方法实现,或通过实现Runnale接口来重写run方法实现。

①  通过继承Thread类来重写run方法实现

package com.test1;

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        Cat cat = new Cat();
        cat.start(); // 启动线程

        // 当主线程启动了子线程后,主线程本身不会阻塞,会继续执行
        // 此时主线程与子线程交替执行
        System.out.println("主线程继续执行:"+Thread.currentThread().getName());
        for(int i = 0; i < 10; i++){
            System.out.println("主线程:"+i);
            Thread.sleep(500); // 主线程休眠0.5s
        }
    }
}

class Cat extends Thread{
    int times = 0;

    @Override
    public void run() {
        while (true){
            System.out.println("喵喵,我是小猫咪"+(++times)+", 线程名:"+Thread.currentThread().getName());
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(times == 80){
                break;
            }
        }
    }
}

通过jconsole监控线程的执行情况:

注:其中cat.start()负责启动线程,最终会执行的是cat对象的run方法。那为什么不直接运行cat.run()呢?原因是,直接执行cat.run()的话,启用的其实是主线程本身去运行run方法,而不是另开子线程,这样就会阻塞在cat.run()这里直至执行完毕,才会按顺序向下继续执行,失去了多线程的意义。此外,通过源码可知,start()方法其实调用的是里面的start0()方法,start0()是本地方法,由JVM负责调用,底层是c/c++实现。所以说,真正实现多线程效果的是start0()而非run()。

②  通过实现Runnale接口来重写run方法实现

package com.test1;

public class test1 {
    public static void main(String[] args) {
        Dog dog = new Dog();
//        dog.start(); // 通过实现Runnable接口得到的对象无法调用start()方法
        // 通过把dog对象(实现了Runnable)放入Thread来实现调用start()方法的目的
        Thread thread = new Thread(dog);
        thread.start();
    }
}

class Dog implements Runnable{
    int times = 0;

    @Override
    public void run() {
        while (true){
            System.out.println("汪汪,我是小狗"+(++times)+", 线程名:"+Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(times == 10){
                break;
            }
        }
    }
}

例一  多线程执行案例

package com.test1;

public class test1 {
    public static void main(String[] args) {
        T1 t1 = new T1();
        T2 t2 = new T2();
        Thread thread1 = new Thread(t1);
        Thread thread2 = new Thread(t2);
        thread1.start();
        thread2.start();
    }
}

class T1 implements Runnable{
    int times = 0;

    @Override
    public void run() {
        while (true){
            System.out.println("hello word: "+(++times));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(times == 6){
                break;
            }
        }
    }
}

class T2 implements Runnable{
    int times = 0;

    @Override
    public void run() {
        while (true){
            System.out.println("hi: "+(++times));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(times == 3){
                break;
            }
        }
    }
}

例二

package com.test1;
/***
 * 使用多线程,模拟3个窗口同时售票100张
 */
public class test1 {
    public static void main(String[] args) {
        SellTicket sellTicket1 =new SellTicket();
        SellTicket sellTicket2 =new SellTicket();
        SellTicket sellTicket3 =new SellTicket();
        sellTicket1.start();
        sellTicket2.start();
        sellTicket3.start();
    }
}

class SellTicket extends Thread{
    private static int ticketNum = 100; // 多线程共享

    @Override
    public void run() {
        while (true){
            if(ticketNum <= 0){
                System.out.println("售票结束!");
                break;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("窗口"+Thread.currentThread().getName()+
                    "售出了一张票,剩余票数"+(--ticketNum));
        }
    }
}

  

注:以上出现了票超卖的问题,这里就需要引入同步机制SynChronized来解决了,具体见7。

2.线程终止

① 通过使用变量来控制run方法退出

package com.test1;

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();

        // 如果希望main线程去控制t1线程的终止,必须可以修改loop
        // 让t1退出run方法,从而终止t1线程 -> 通知方式
        System.out.println("主线程休眠0.5s");
        Thread.sleep(500);
        t.setLoop(false);
    }
}

class T extends Thread{
    private int count = 0;
    private boolean loop = true;

    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    @Override
    public void run() {
        while (loop){
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("T运行中"+(count++));
        }
    }
}

3.线程中断

package com.test1;

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.setName("测试");
        t.setPriority(Thread.MAX_PRIORITY);
        t.start();

        for(int i = 0; i < 5; i++){
            Thread.sleep(1000);
            System.out.println("hi"+i);
        }
        t.interrupt(); // 中断t线程的休眠
    }
}

class T extends Thread{
    @Override
    public void run() {
        while (true){
            for(int i = 0; i < 20; i++){
                System.out.println(Thread.currentThread().getName()+"吃包子"+i);
            }
            try {
                System.out.println(Thread.currentThread().getName()+"休眠中");
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                // 当该线程执行到一个interrupt方法时会catch到一个异常,从而方便在此加入自己的业务代码
                System.out.println(Thread.currentThread().getName()+"被interrupt了");
            }
        }
    }
}

4.线程插队   yield与join方法

① join线程插队

package com.test1;

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();

        for(int i = 1; i <= 8; i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("主线程(小弟)吃了"+i+"个包子");
            if(i == 4){
                System.out.println("主线程(小弟)让子线程(老大)先吃完!");
                t.join(); // 插队
                System.out.println("子线程(老大)吃完了,主线程(小弟)接着吃。");
            }
        }
    }
}

class T extends Thread{
    @Override
    public void run() {
        for(int i = 1; i <= 8; i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("子线程(大哥)吃了"+i+"个包子");
        }
    }
}

② yield线程礼让

package com.test1;

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();

        for(int i = 1; i <= 8; i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("主线程(小弟)吃了"+i+"个包子");
            if(i == 4){
                System.out.println("主线程(小弟)让子线程(老大)先吃完!");
//                t.join(); // 插队
                Thread.yield(); // 线程礼让,不一定会成功,取决于当前内核态资源是否充足
//                System.out.println("子线程(老大)吃完了,主线程(小弟)接着吃。");
            }
        }
    }
}

class T extends Thread{
    @Override
    public void run() {
        for(int i = 1; i <= 8; i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("子线程(大哥)吃了"+i+"个包子");
        }
    }
}

  

5.守护线程

package com.test1;

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        MyDaemonThread myDaemonThread = new MyDaemonThread();
        // 若想当主线程结束的同时,子线程能自动结束,则只需把子线程设置为守护线程
        myDaemonThread.setDaemon(true);
        myDaemonThread.start();

        for(int i = 0; i < 6; i++){
            System.out.println("主线程在运行");
            Thread.sleep(1000);
        }
    }
}

class MyDaemonThread extends Thread{
    @Override
    public void run() {
        for(;;){ // 无限循环
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("MyDaemonThread在运行");
        }
    }
}

6.线程的生命周期

package com.test1;

public class test1 {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        System.out.println(t.getName()+" 状态 "+t.getState());
        t.start();
        while (t.getState() != Thread.State.TERMINATED){
            System.out.println(t.getName()+" 状态 "+t.getState());
            Thread.sleep(500);
        }
        System.out.println(t.getName()+" 状态 "+t.getState());
    }
}

class T extends Thread{
    @Override
    public void run() {
        for(int i = 0; i < 6; i++){
            System.out.println("hi:"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

7.线程的同步机制 Synchronized

涉及多线程共享资源的情况,要用实现Runnable接口的方式而不能用继承Thread类!

正确方法:

package com.test1;
/***
 * 使用多线程,模拟3个窗口同时售票100张
 */
public class test1 {
    public static void main(String[] args) {
        SellTicket sellTicket1 = new SellTicket();
        new Thread(sellTicket1).start();
        new Thread(sellTicket1).start();
        new Thread(sellTicket1).start();
    }
}

//class SellTicket extends Thread{
class SellTicket implements Runnable{
    private static int ticketNum = 100; // 多线程共享
    private boolean loop = true;

    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    // 同步方法,保证在同一时刻只能有一个线程在执行run方法
    // 此时锁在this对象
    // 也可在代码块上写synchronized,实现同步代码块
    public synchronized void sell(){
        if(ticketNum <= 0){
            setLoop(false);
            System.out.println("售票结束!");
            return;
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("窗口"+Thread.currentThread().getName()+
                "售出了一张票,剩余票数"+(--ticketNum));
    }

    @Override
    public void run() {
        while (loop){
            sell();
        }
    }
}

错误方法:

package com.test1;
/***
 * 使用多线程,模拟3个窗口同时售票100张
 */
public class test1 {
    public static void main(String[] args) {
        SellTicket sellTicket1 = new SellTicket();
        SellTicket sellTicket2 =new SellTicket();
        SellTicket sellTicket3 =new SellTicket();
        sellTicket1.start();
        sellTicket2.start();
        sellTicket3.start();
    }
}

class SellTicket extends Thread{
//class SellTicket implements Runnable{
    private static int ticketNum = 100; // 多线程共享
    private boolean loop = true;

    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    // 同步方法,保证在同一时刻只能有一个线程在执行run方法
    public synchronized void sell(){
        if(ticketNum <= 0){
            setLoop(false);
            System.out.println("售票结束!");
            return;
        }
        try {
            Thread.sleep(50);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("窗口"+Thread.currentThread().getName()+
                "售出了一张票,剩余票数"+(--ticketNum));
    }

    @Override
    public void run() {
        while (loop){
            sell();
        }
    }
}

8.互斥锁

上7是通过同步方法实现的,这次用同步代码块实现:

package com.test1;
/***
 * 使用多线程,模拟3个窗口同时售票100张
 */
public class test1 {
    public static void main(String[] args) {
        SellTicket sellTicket1 = new SellTicket();
        new Thread(sellTicket1).start();
        new Thread(sellTicket1).start();
        new Thread(sellTicket1).start();
    }
}

//class SellTicket extends Thread{
class SellTicket implements Runnable{
    private static int ticketNum = 100; // 多线程共享
    private boolean loop = true;

    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    // 同步代码块
    public void sell(){
//        synchronized (this){ // 也可
        synchronized (SellTicket.class){
            if(ticketNum <= 0){
                setLoop(false);
                System.out.println("售票结束!");
                return;
            }
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("窗口"+Thread.currentThread().getName()+
                    "售出了一张票,剩余票数"+(--ticketNum));
        }
    }

    @Override
    public void run() {
        while (loop){
            sell();
        }
    }
}

注:以上方法如果是静态的,如 public static void sell(){ },则只能是

synchronized (SellTicket.class){ },而不能是synchronized (this){ }

9.死锁

package com.test1;
/***
 * 使用多线程,模拟3个窗口同时售票100张
 */
public class test1 {
    public static void main(String[] args) {
        // 模拟死锁现象
        DeadLock A = new DeadLock(true);
        A.setName("A线程");
        DeadLock B = new DeadLock(false);
        B.setName("B线程");
        A.start();
        B.start();
    }
}

class DeadLock extends Thread{
    // static用于保证多线程共享同一对象
    static Object o1 = new Object();
    static Object o2 = new Object();
    private boolean flag;

    public DeadLock(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        // 当flag为true,线程A会先占有o1对象锁,然后尝试获取o2对象锁。此时如果o2被占用,则线程A会阻塞。反之亦然。
        if(flag){
            synchronized (o1){ // 对象互斥锁
                System.out.println(Thread.currentThread().getName()+"进入1");
                synchronized (o2){
                    System.out.println(Thread.currentThread().getName()+"进入2");
                }
            }
        }else {
            synchronized (o2){
                System.out.println(Thread.currentThread().getName()+"进入3");
                synchronized (o1){
                    System.out.println(Thread.currentThread().getName()+"进入4");
                }
            }
        }
    }
}

10.释放锁

综合练习

例1

package com.test1;

import java.util.Scanner;

public class test1 {
    public static void main(String[] args) {
        A a = new A();
        B b = new B(a);
        a.start();
        b.start();
    }
}

class A extends Thread{
    private boolean flag = true;

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        while (flag){
            System.out.println((int)(Math.random()*100+1));
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

class B extends Thread{
    private A a;
    private Scanner scanner = new Scanner(System.in);

    public B(A a) { // 构造器直接传入A类对象
        this.a = a;
    }

    @Override
    public void run() {
        while (true){
            System.out.println("请输入指令(Q)表示退出:");
            char key = scanner.next().toUpperCase().charAt(0);
            if(key == 'Q'){
                // 以通知的方式结束线程A
                a.setFlag(false);
                System.out.println("B线程退出。");
                break;
            }
        }
    }
}

例2

package com.test1;

public class test1 {
    public static void main(String[] args) {
        T t = new T();
        // 注意多线程thread1、thread2共用对象t
        Thread thread1 = new Thread(t);
        thread1.setName("t1");
        thread1.start();
        Thread thread2 = new Thread(t);
        thread2.setName("t2");
        thread2.start();
    }
}

// 涉及多线程共享资源,所以使用实现Runnable接口的方式
class T implements Runnable{
    private static int money = 10000;
    private boolean loop = true;

    public void setLoop(boolean loop) {
        this.loop = loop;
    }

    public synchronized void tmp(){
        if(money < 1000){
            System.out.println("余额不足");
            setLoop(false);
            return;
        }
        money -= 1000;
        System.out.println(Thread.currentThread().getName()+
                "取了1000,当前余额:"+money);
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void run() {
        while (loop){
            tmp();
        }
    }
}

二. 线程池
几种常见的线程池及使用场景_青藤伽的博客-CSDN博客_线程池选择

1.自定义线程池

Executors和ThreadPoolExecutor详解_China渔火的博客-CSDN博客_executors和threadpoolexecutor

在《阿里巴巴java开发手册》中指出了线程资源必须通过线程池提供,不允许在应用中自行显示的创建线程,这样一方面是线程的创建更加规范,可以合理控制开辟线程的数量;另一方面线程的细节管理交给线程池处理,优化了资源的开销。而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,但都有其局限性,不够灵活;另外由于前面几种方法内部也是通过ThreadPoolExecutor方式实现,使用ThreadPoolExecutor有助于大家明确线程池的运行规则,创建符合自己的业务场景需要的线程池,避免资源耗尽的风险。

线程池——Executor、Executors、ExecutorService、ThreadPoolExecutor、ThreadPoolTaskExecutor之间的区别_懒鸟一枚的博客-CSDN博客

  

① 理论

Executors中创建线程池的快捷方法,实际上是调用了ThreadPoolExecutor的构造方法(定时任务使用的是ScheduledThreadPoolExecutor):

②. 实现

MyTask.java 任务类

package com.test0;
/*
* 自定义线程池,这是任务类,实现接口Runnable;
* 包含任务编号,假设每个任务的执行时间为0.2s
* */
public class MyTask implements Runnable{
    private int id;

    // id这个属性的初始化可以用构造方法实现
    public MyTask(int id){
        this.id = id;
    }

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println("线程:"+name+" 即将执行任务:"+id);
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("线程:"+name+" 完成了任务:"+id);
    }

    @Override
    public String toString() {
        return "MyTask{" +
                "id=" + id +
                '}';
    }
}
MyWorker.java 线程类
package com.test0;

import java.util.List;

/*
* 编写一个线程类,继承Thread类;
* 设计一个属性,用于保存线程名称;
* 设计一个集合,用于保存所有的任务。
* */
public class MyWorker extends Thread {
    private String name; // 线程名称
    private List<Runnable> tasks;

    public MyWorker(String name, List<Runnable> tasks) {
        super(name);
        this.tasks = tasks;
    }

    @Override
    public void run() {
        // 判断集合里是否有任务,有就一直执行
        while (tasks.size() > 0){
            Runnable r = tasks.remove(0);
            r.run();
        }
    }
}
MyThreadPool.java 线程池类
package com.test0;

import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

/*
* 自定义的线程池类
* 成员变量:
*   1.任务队列 集合 需要控制线程安全问题
*   2.当前线程数量
*   3.核心线程数量
*   4.最大线程数量
*   5.任务队列的长度
* 成员方法:
*   1.提交任务(把任务添加到集合,需判断是否超出任务最大数量)
*   2.执行任务(根据当前线程数量决定创建核心线程还是非核心线程)
* */
public class MyThreadPool {
    // 1.任务队列 集合 需要控制线程安全问题
    // Collections.synchronizedList()用于实现List的线程安全
    private List<Runnable> tasks = Collections.synchronizedList(new LinkedList<>());
    // 2.当前线程数量
    private int num;
    // 3.核心线程数量
    private int corePoolSize;
    // 4.最大线程数量
    private int maxSize;
    // 5.任务队列的长度
    private int workSize;

    public MyThreadPool(int corePoolSize, int maxSize, int workSize) {
        this.corePoolSize = corePoolSize;
        this.maxSize = maxSize;
        this.workSize = workSize;
    }

    // 1.提交任务
    public void submit(Runnable r){
        // 判断当前集合中的任务数量是否超出任务最大数量
        if(tasks.size() >= workSize){
            System.out.println("任务"+r+"被丢弃了!");
        }else {
            tasks.add(r);
            execTask(r);
        }
    }

    // 2.执行任务
    private void execTask(Runnable r) {
        // 判断当前线程池中的线程总数量,是否超出了核心数
        if(num < corePoolSize){
            new MyWorker("核心线程:"+num, tasks).start();
            num++;
        }else if(num < maxSize){
            new MyWorker("非核心线程:"+num, tasks).start();
            num++;
        }else {
            System.out.println("任务: "+r+"被缓存了!");
        }
    }
}
MyTest.java 测试类
package com.test0;
/*
* 测试类:
*   1.创建线程池类对象
*   2.提交多个任务
* */
public class MyTest {
    public static void main(String[] args) {
        // 1.创建线程池类对象
        MyThreadPool myThreadPool = new MyThreadPool(2, 4, 20);
        // 2.提交多个任务
        for (int i = 0; i < 10; i++){
            // 3.创建任务对象,并提交给线程池
            MyTask my = new MyTask(i);
            myThreadPool.submit(my);
        }
    }
}

2. java内置线程池

(一)ExecutorService与scheduleExecutorService接口

newCachedThreadPool性能优先,newSingleThreadExecutor单线程保证绝对安全:

(1). ExecutorService的submit()方法

① newCachedThreadPool()

package com.test1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
* 练习Executors获取ExecutorService,然后调用方法,提交任务
* */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newCachedThreadPool();
        // 2.提交任务
        for(int i = 1; i <= 10; i++){
            es.submit(new MyRunnable(i));
        }
    }
}

/*
* 任务类,包含一个任务编号
* */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }
}

②  newCachedThreadPool(new ThreadFactory(){ })

package com.test1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/*
* 练习Executors获取ExecutorService,然后调用方法,提交任务
* */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newCachedThreadPool(new ThreadFactory() {
            int i = 1;

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "自定义的线程名称" + i++);
            }
        });
        // 2.提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable(i));
        }
    }
}

/*
* 任务类,包含一个任务编号
* */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }
}

③ newFixedThreadPool(int nThreads)

package com.test1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
 * 练习Executors获取ExecutorService,然后调用方法,提交任务
 * */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newFixedThreadPool(3);
        // 2.提交任务
        for(int i = 1; i <= 10; i++){
            es.submit(new MyRunnable(i));
        }
    }
}

/*
 * 任务类,包含一个任务编号
 * */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }
}

④ newFixedThreadPool(int nThreads, new ThreadFactory() { })

package com.test1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/*
 * 练习Executors获取ExecutorService,然后调用方法,提交任务
 * */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newFixedThreadPool(3, new ThreadFactory() {
            int i = 1;

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "自定义的线程名称" + i++);
            }
        });
        // 2.提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable(i));
        }
    }
}

/*
 * 任务类,包含一个任务编号
 * */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }
}

⑤ newSingleThreadExecutor()

package com.test1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
 * 练习Executors获取ExecutorService,然后调用方法,提交任务
 * */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newSingleThreadExecutor();
        // 2.提交任务
        for(int i = 1; i <= 10; i++){
            es.submit(new MyRunnable(i));
        }
    }
}

/*
 * 任务类,包含一个任务编号
 * */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }
}

⑥ newSingleThreadExecutor(new ThreadFactory(){ })

package com.test1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

/*
 * 练习Executors获取ExecutorService,然后调用方法,提交任务
 * */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {
            int i = 1;

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "自定义的线程名称" + i++);
            }
        });
        // 2.提交任务
        for (int i = 1; i <= 10; i++) {
            es.submit(new MyRunnable(i));
        }
    }
}

/*
 * 任务类,包含一个任务编号
 * */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }
}

  

(2). ExecutorService的shutdown()与shutdownNow()方法

①. shutdown()

package com.test1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
 * 练习Executors获取ExecutorService,测试关闭线程池的方法
 * */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newSingleThreadExecutor();
        // 2.提交任务
        for(int i = 1; i <= 10; i++){
            es.submit(new MyRunnable(i));
        }
        // 3.关闭线程池,仅仅是不会再接收新的任务,之前的任务不受影响
        es.shutdown();
    }
}

/*
 * 任务类,包含一个任务编号
 * */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }
}

package com.test1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
 * 练习Executors获取ExecutorService,测试关闭线程池的方法
 * */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newSingleThreadExecutor();
        // 2.提交任务
        for(int i = 1; i <= 10; i++){
            es.submit(new MyRunnable(i));
        }
        // 3.关闭线程池,仅仅是不会再接收新的任务,之前的任务不受影响
        es.shutdown();
        es.submit(new MyRunnable(66)); // 错,不能再提交新任务了
    }
}

/*
 * 任务类,包含一个任务编号
 * */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }
}

②. shutdownNow()

package com.test1;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/*
 * 练习Executors获取ExecutorService,测试关闭线程池的方法
 * */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.使用工厂类获取线程池对象
        ExecutorService es = Executors.newSingleThreadExecutor();
        // 2.提交任务
        for(int i = 1; i <= 10; i++){
            es.submit(new MyRunnable(i));
        }
        // 3.立即关闭线程池,如果线程池中还有缓存的任务尚未执行,则取消执行并返回这些任务
        List<Runnable> list = es.shutdownNow();
        System.out.println(list);
    }
}

/*
 * 任务类,包含一个任务编号
 * */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }

    @Override
    public String toString() {
        return "MyRunnable{" +
                "id=" + id +
                '}';
    }
}

(3). scheduleExecutorService

① newScheduledThreadPool(int corePoolSize),schedule()

package com.test1;

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/*
 * 测试ScheduledExecutorService接口中延迟执行任务和重复执行任务的功能
 * */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.获取一个具备延迟执行任务的线程池对象
        ScheduledExecutorService es = Executors.newScheduledThreadPool(3);
        // 2.创建多个任务对象,提交任务,每个任务延迟2s执行
        for(int i = 1; i <= 10; i++){
            es.schedule(new MyRunnable(i), 2, TimeUnit.SECONDS);
        }
        System.out.println("Over!");
    }
}

/*
 * 任务类,包含一个任务编号
 * */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }

    @Override
    public String toString() {
        return "MyRunnable{" +
                "id=" + id +
                '}';
    }
}

② newScheduledThreadPool(int corePoolSize, new ThreadFactory(){ }),schedule()

package com.test1;

import java.util.concurrent.*;

/*
 * 练习Executors获取ExecutorService,然后调用方法,提交任务
 * */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.获取一个具备延迟执行任务的线程池对象
        ScheduledExecutorService es = Executors.newScheduledThreadPool(3, new ThreadFactory() {
            int i = 1;

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "自定义的线程名称" + i++);
            }
        });
        // 2.创建多个任务对象,提交任务,每个任务延迟2s执行
        for (int i = 1; i <= 10; i++) {
            es.schedule(new MyRunnable(i), 2, TimeUnit.SECONDS);
        }
    }
}

/*
 * 任务类,包含一个任务编号
 * */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        System.out.println(name+"执行了任务:"+id);
    }
}

③ scheduleAtFixedRate()

package com.test1;

import java.util.concurrent.*;

/*
 * 练习Executors获取ExecutorService,然后调用方法,提交任务
 * */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.获取一个具备延迟执行任务的线程池对象
        ScheduledExecutorService es = Executors.newScheduledThreadPool(3, new ThreadFactory() {
            int i = 1;

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "自定义的线程名称" + i++);
            }
        });
        // 2.创建任务对象,提交任务,每个任务延迟2s执行
        es.scheduleAtFixedRate(new MyRunnable(1), 1, 2, TimeUnit.SECONDS);
    }
}

/*
 * 任务类,包含一个任务编号
 * */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        try {
            Thread.sleep(1500);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(name+"执行了任务:"+id);
    }
}

④ scheduleWithFixedDelay()

package com.test1;

import java.util.concurrent.*;

/*
 * 练习Executors获取ExecutorService,然后调用方法,提交任务
 * */
public class MyTest01 {
    public static void main(String[] args) {
        // 1.获取一个具备延迟执行任务的线程池对象
        ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
            int i = 1;

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "自定义的线程名称" + i++);
            }
        });
        // 2.创建任务对象,提交任务,每个任务延迟2s执行
        es.scheduleWithFixedDelay(new MyRunnable(1), 1, 2, TimeUnit.SECONDS);
        System.out.println("over!");
    }
}

/*
 * 任务类,包含一个任务编号
 * */
class MyRunnable implements Runnable{
    private int id;

    public MyRunnable(int id) {
        this.id = id;
    }

    @Override
    public void run() {
        // 获取线程名称
        String name = Thread.currentThread().getName();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(name+"执行了任务:"+id);
    }
}

(二)异步计算结果(Future)

(1)

① isDone(),isCancelled(),get()

package com.test1;

import java.util.concurrent.*;

public class MyTest01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1.获取线程池对象
        ExecutorService es = Executors.newCachedThreadPool();
        // 2.创建Callable类型的任务对象
        Future<Integer> f = es.submit(new MyCall(1, 1));
        // 3.判断任务是否已经完成
        Boolean done = f.isDone();
        System.out.println("首次判断任务是否已经完成:"+done);
        Boolean cancelled = f.isCancelled();
        System.out.println("首次判断任务是否已经取消:"+cancelled);
        Integer v = f.get();
        System.out.println("任务执行结果:"+v);
        Boolean done2 = f.isDone();
        System.out.println("第二次判断任务是否已经完成:"+done2);
        Boolean cancelled2 = f.isCancelled();
        System.out.println("第二次判断任务是否已经取消:"+cancelled2);
    }
}

class MyCall implements Callable<Integer>{
    private int a;
    private int b;

    public MyCall(int a, int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public Integer call() throws Exception {
        String name = Thread.currentThread().getName();
        System.out.println(name+"准备开始计算");
        Thread.sleep(2000);
        System.out.println(name+"计算完成");
        return a+b;
    }
}

② cancel()

package com.test1;

import java.util.concurrent.*;

public class MyTest01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 1.获取线程池对象
        ExecutorService es = Executors.newCachedThreadPool();
        // 2.创建Callable类型的任务对象
        Future<Integer> f = es.submit(new MyCall(1, 1));
        // 3.取消任务
        Boolean b = f.cancel(true);
        System.out.println("是否取消任务成功:"+b);
    }
}

class MyCall implements Callable<Integer>{
    private int a;
    private int b;

    public MyCall(int a, int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public Integer call() throws Exception {
        String name = Thread.currentThread().getName();
        System.out.println(name+"准备开始计算");
        Thread.sleep(2000);
        System.out.println(name+"计算完成");
        return a+b;
    }
}

③ get(long timeout, TimeUnit unit)

package com.test1;

import java.util.concurrent.*;

public class MyTest01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        // 1.获取线程池对象
        ExecutorService es = Executors.newCachedThreadPool();
        // 2.创建Callable类型的任务对象
        Future<Integer> f = es.submit(new MyCall(1, 1));
        // 3.获取结果
        Integer v = f.get(3, TimeUnit.SECONDS);
        System.out.println("任务执行结果:"+v);
    }
}

class MyCall implements Callable<Integer>{
    private int a;
    private int b;

    public MyCall(int a, int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public Integer call() throws Exception {
        String name = Thread.currentThread().getName();
        System.out.println(name+"准备开始计算");
        Thread.sleep(2000);
        System.out.println(name+"计算完成");
        return a+b;
    }
}

但是,当get()的等待时间少于执行时间,即开始执行获取指令时还没来得及执行完毕,则会报错:

package com.test1;

import java.util.concurrent.*;

public class MyTest01 {
    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        // 1.获取线程池对象
        ExecutorService es = Executors.newCachedThreadPool();
        // 2.创建Callable类型的任务对象
        Future<Integer> f = es.submit(new MyCall(1, 1));
        // 3.获取结果
        Integer v = f.get(1, TimeUnit.SECONDS);
        System.out.println("任务执行结果:"+v);
    }
}

class MyCall implements Callable<Integer>{
    private int a;
    private int b;

    public MyCall(int a, int b) {
        this.a = a;
        this.b = b;
    }

    @Override
    public Integer call() throws Exception {
        String name = Thread.currentThread().getName();
        System.out.println(name+"准备开始计算");
        Thread.sleep(2000);
        System.out.println(name+"计算完成");
        return a+b;
    }
}

  

线程池综合案例

1.秒杀商品

MyTask.java
package com.test1;
/**
 * 任务类:包含商品数量,客户名,送手机的行为
 */
public class MyTask implements Runnable{
    private static int id = 10; // 商品数量
    private String userName; // 客户名

    public MyTask(String userName) {
        this.userName = userName;
    }

    @Override
    public void run() {
        String taskname = Thread.currentThread().getName();
        System.out.println(userName+"正在使用"+taskname+"参与秒杀任务");
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        synchronized (MyTask.class){ // 控制线程安全,防止出现错误数据
            if(id > 0){
                System.out.println(userName+"使用"+taskname+"秒杀:"+id--+"号商品成功!");
            }else {
                System.out.println(userName+"使用"+taskname+"秒杀失败!");
            }
        }
    }
}
MyTest.java
package com.test1;

import javafx.concurrent.Task;
/**
 * 测试类
 */
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class MyTest {
    public static void main(String[] args) {
        // 1.创建一个线程池对象
        ThreadPoolExecutor pool = new ThreadPoolExecutor(3,5,1,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(15));
        // 2.循环创建任务对象
        for(int i = 0; i < 20; i++){
            MyTask myTask = new MyTask("客户"+i);
            pool.submit(myTask);
        }
        // 3.关闭线程池
        pool.shutdown();
    }
}

  

2.取款任务

MyTask.java

package com.test1;

public class MyTask implements Runnable{
    private String usrName; // 用户名
    private Double money; // 取款额
    private static double total = 1000; // 总金额

    public MyTask(String usrName, Double money) {
        this.usrName = usrName;
        this.money = money;
    }

    @Override
    public void run() {
        String taskName = Thread.currentThread().getName();
        System.out.println(usrName+"正在使用"+taskName+"取款"+money+"元");
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        synchronized (MyTask.class){ // 控制线程池安全
            if((total-money) > 0){
                System.out.println(usrName+"使用"+taskName+"成功取款"+money+
                        "元, 余额"+(total-money)+"元");
                total -= money;
            }else {
                System.out.println(usrName+"使用"+taskName+"取款"+money+
                        "元失败, 余额"+total+"元");
            }
        }
    }
}

 MyTest.java

package com.test1;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

public class MyTest {
    public static void main(String[] args) {
        // 1.创建线程池对象
        ExecutorService es = Executors.newFixedThreadPool(2, new ThreadFactory() {
            int id = 1;

            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "ATM"+id++);
            }
        });
        // 2.创建两个任务并提交
        for(int i = 1; i < 3; i++){
            MyTask myTask = new MyTask("客户"+i, 800.0);
            es.submit(myTask);
        }
        // 3.关闭线程池
        es.shutdown();
    }
}

  

三.异步

1.CompletableFuture

例1

CompletableFuture实现多个互不相干的方法并行调用_轻尘×的博客-CSDN博客_completablefuture并行处理

串行时间就是各方法执行时间的总和,而并行时间几乎和每一个方法执行的时间相同(用CompletableFuture的supplyAsync异步调用方法,相当于每个方法单独起一个线程去执行,最后用 CompletableFuture.allOf方法等待所有方法执行完毕,然后取出结果,这样所有接口的耗时就取决于最慢的那个服务/方法)。 

package com.test0;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.*;

public class test0 {
    static ExecutorService executorService = Executors.newFixedThreadPool(7);

    // 异步并行
    public static Map dataStatisticsParallel() {
        CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> countUserNum(), executorService);
        CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> countCompanyNum(), executorService);
        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> countWorkNum(), executorService);
        CompletableFuture<Integer> future4 = CompletableFuture.supplyAsync(() -> countTaskNum(), executorService);
        CompletableFuture<Integer> future5 = CompletableFuture.supplyAsync(() -> countCourseNum(), executorService);
        CompletableFuture<Integer> future6 = CompletableFuture.supplyAsync(() -> countMissonNum(), executorService);
        CompletableFuture<Integer> future7 = CompletableFuture.supplyAsync(() -> countLoginNum(), executorService);

        // 阻塞,等待所有任务执行完成
        CompletableFuture<Void> allOf = CompletableFuture.allOf(future1, future2,
                future3, future4, future5, future6, future7);
        try {
            allOf.get();

            Map<String, Object> data = new HashMap<>();
            data.put("userNum", future1.get());
            data.put("companyNum", future2.get());
            data.put("workNum", future3.get());
            data.put("taskNum", future4.get());
            data.put("courseNum", future5.get());
            data.put("missionNum", future6.get());
            data.put("loginNum", future7.get());

            return data;


        } catch (InterruptedException exception) {

        } catch (ExecutionException exception) {

        } finally {
            executorService.shutdown();
        }

        return null;
    }


    public static void main(String[] args) {
        System.out.println("---串行开始----");
        Map map = dataStatistics();          // 串行
        map.forEach((key, value) -> System.out.println(key + ":" + value));
        System.out.println("---串行结束----" );

        System.out.println("---并行开始----");
        Map map1 = dataStatisticsParallel(); // 异步并行
        map1.forEach((key, value) -> System.out.println(key + ":" + value));
        System.out.println("---并行结束----" );
    }

    /**
     * 首页大屏数据统计,每个模拟耗时1秒
     */
    // 串行
    public static Map dataStatistics() {
        Map<String, Object> data = new HashMap<>();
        data.put("userNum", countUserNum());
        data.put("companyNum", countCompanyNum());
        data.put("workNum", countWorkNum());
        data.put("taskNum", countTaskNum());
        data.put("courseNum", countCourseNum());
        data.put("missionNum", countMissonNum());
        data.put("loginNum", countLoginNum());

        return data;
    }

    private static Integer countLoginNum() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException exception) {
            exception.printStackTrace();
        }
        Integer countLoginNum = new Random().nextInt(1000);
        return countLoginNum == null ? 0 : countLoginNum * 5;
    }

    private static Integer countMissonNum() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException exception) {
            exception.printStackTrace();
        }
        Integer countMissonNum = new Random().nextInt(1000);
        return countMissonNum == null ? 0 : countMissonNum;
    }

    private static Integer countCourseNum() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException exception) {
            exception.printStackTrace();
        }
        Integer countCourseNum = new Random().nextInt(1000);
        return countCourseNum == null ? 0 : countCourseNum;
    }

    private static Integer countTaskNum() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException exception) {
            exception.printStackTrace();
        }
        Integer countTaskNum = new Random().nextInt(1000);
        return countTaskNum == null ? 0 : countTaskNum;
    }

    private static Integer countWorkNum() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException exception) {
            exception.printStackTrace();
        }
        Integer countWorkNum = new Random().nextInt(1000);
        return countWorkNum == null ? 0 : countWorkNum;
    }

    private static Integer countCompanyNum() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException exception) {
            exception.printStackTrace();
        }

        Integer countCompanyNum = new Random().nextInt(1000);
        return countCompanyNum == null ? 0 : countCompanyNum;
    }

    private static Integer countUserNum() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException exception) {
            exception.printStackTrace();
        }

        Integer countUserNum = new Random().nextInt(1000);
        return countUserNum == null ? 0 : countUserNum;
    }

}

例2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值