Java多线程

本文详细介绍了Java中的多线程概念,包括并发与并行、进程与线程的区别,以及线程调度的原理。接着讲解了创建线程的两种方式:继承Thread类和实现Runnable接口,并通过代码示例展示了如何使用。还深入讨论了线程安全、同步机制,如synchronized关键字、Lock锁以及等待唤醒机制。最后,文章探讨了线程池的概念和使用,强调了其在资源管理中的重要性。
摘要由CSDN通过智能技术生成

Java多线程

一、基本概念
1、并发与并行

并发:两个或多个事件在同一时间段内发生

并行:两个或多个事件在同一时间发生

2、进程

进程是指一个在内存中运行的应用程序,每个进程都有一个独立的内存空间,一个应用程序可以同时运行多个进行;进程也是程序的一次执行过程,是系统运行程序的基本单位;系统运行一个程序即是一个进程从创建、运行到消亡的过程。

3、线程

线程是进程的一个执行单元,负责当前进程中程序的执行。一个程序运行后至少有一个进程,一个进程中可以包含多个线程

4、线程调度
  • 分时调度

    所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。

  • 抢占式调度

    优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。

二、多线程
1、创建多线程的两种方式

(1)创建Thread的子类

  • 创建一个Thread类的子类

  • 在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么)

  • 创建Thread类的子类对象

  • 调用Thread类中的start方法,开启新的线程,执行run方法

    void start() 使线程开始执行;Java虚拟机调用该线程的run方法,该线程和主线程并发运行,该线程执行run方法。

注意:

  • 多次启动一个线程是非法的,特别适当线程已经结束,不能再重新启动
  • Java程序属于抢占式调度,哪个线程的优先级搞,哪个线程优先执行,同一个优先级,随机执行

代码实现:

//1、创建一个Thread类的子类
public class MyThread extends Thread{
    //2、在Thread类的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么)
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("run:" + i);
        }
    }
}
// 测试类
public class Demo01Thread {
    public static void main(String[] args) {
        //3、创建Thread类的子类对象
        MyThread myThread = new MyThread();
        //4、调用Thread类中的方法start,开启新的线程,执行run方法
        myThread.start();
        for (int i = 0; i < 20; i++) {
            System.out.println("main:" + i);
        }
    }
}

(2)实现Runnable接口

java.lang.Runnable
Runnable接口应该由那些打算通过某一线程执行其实例来实现,类必须定义一个称为run的无参数方法
java.lang.Thread类的构造方法
Thread(Runnable target) 分配新的Thread 对象
Thread(Runnable target,String name)分配的Thread对象

实现步骤

  • 创建一个Runnable接口的实现类
  • 在实现类中重写Runnable接口的run方法,设置线程任务
  • 创建一个Runnable接口的实现类对象
  • 创建Thread类对象,构造方法中传递Runnable接口的实现类对象
  • 调用Thread类中start方法,开启新的线程执行run方法

代码实现

// 1、创建一个Runnable接口的实现类
public class RunnableImpl implements Runnable{
    // 2、在实现类中重写Runnable接口的run方法,设置线程任务
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println(Thread.currentThread().getName() + "---->" + i);
        }
    }
}
public class Demo01Runnable {
    public static void main(String[] args) {
        // 3、创建一个Runnable接口的实现类对象
        RunnableImpl ri = new RunnableImpl();
        // 4、创建Thread类对象,构造方法中传递Runnable接口的实现类对象
        Thread t = new Thread(ri);
        // 5、调用Thread类中的start方法,开启新的线程执行run方法
        t.start();
        System.out.println(Thread.currentThread().getName());
    }
}

实现Runnable接口比继承Thread类所具有的的优势

  • 避免了单继承的局限性
    • 一个类只能继承一个类,如果继承了Thread类就不能继承其他类,实现Runnable接口,还可以继承其他的类,实现其他的接口
  • 增强了程序的扩展性,降低了程序的耦合性(解耦)
    • 实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)
    • 实现类类中,重写了run方法:用来设置线程任务
    • 创建Thread类对象,调用start方法:用来开启新线程
2、多线程原理及内存图解

继承Thread类的线程类,每创建一次实例并调用start方法就在栈内存开辟新的空间来执行run方法,CPU可以选择执行main方法或者run方法。
在这里插入图片描述

3、Thread类的常用方法

(1)获取线程

String getName()返回该线程的名称

static Thread currentThread()返回对当前正在执行的线程对象的引用

代码实现

public class MyThread extends Thread {
    // 重写Thread类中的run方法,设置线程任务
    @Override
    public void run() {
        // 获取线程名称
        Thread t = Thread.currentThread();
        System.out.println(t);
        String name = t.getName();
        System.out.println(name);
    }
}
public class Demo01GetThreadName {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
        // 链式编程
        System.out.println(Thread.currentThread().getName());// main
    }
}

(2)设置线程名称

void setName(String name)改变线程名称,使之与参数name名称相同

Thread(String name)分配新的Thread对象

代码实现

public class MyThread extends Thread {
    public MyThread(){}
    public MyThread(String name) {
        super(name); // 把线程名称传递给父类,让父类(Thread)给子线程起一个名字
    }
    @Override
    public void run() {
        // 获取线程的名称
        System.out.println(Thread.currentThread().getName());
        setName("赵晓");
        System.out.println(Thread.currentThread().getName());
    }
}
public class Demo01SetThreadName {
    public static void main(String[] args) {
        // 使用构造方法创建自定义线程类实例
        MyThread mt = new MyThread("xiaozhao");
        System.out.println(mt.getName());// xiaozhao
        System.out.println(Thread.currentThread().getName());// main
    }
}

(3)sleep

public static void sleep(long millis) 使当前正在执行的线程以指定的毫秒暂停(暂时停止执行)

代码实现

public class Demo01Sleep {
    public static void main(String[] args) {
        // 模拟秒表
        for (int i = 1; i <= 60; i++) {
            System.out.println(i);
            // 使用Thead类的sleep方法让程序睡眠1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
4、匿名内部类创建线程

匿名内部类的作用:简化代码(相当于新建了一个子类对象/接口实现类对象)

  • 把子类继承父类、重写父类的方法,创建子类对象合成一步完成
  • 把实现实现类接口、重写接口中的方法,创建实现类对象合成一步完成

匿名内部类的最终产物:子类对象/实现类对象,这个类没有名字

代码实现

public class Demo01InnerClassThread {
    public static void main(String[] args) {
        // 线程的父类是Thread
        // new MyThread().start();
        new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName() + "---->" + "赵晓");
                }
            }
        }.start();
        // 线程的接口Runnable
        // Runnable r = new RunnableImpl(); // 多态
        Runnable r = new Runnable() {
            // 重写run方法,设置线程任务
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName() + "---->" + "xiaozhao");
                }
            }
        };
        Thread t = new Thread(r);
        t.start();
        // 链式编程
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 20; i++) {
                    System.out.println(Thread.currentThread().getName() + "---->" + "zhao");
                }
            }
        }).start();

    }
}
5、线程安全及同步
(1)概述

如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。

(2)代码实现

模拟卖票案例的代码实现

public class RunnableImpl implements Runnable {
    private int ticket = 100;
    // 设置线程任务:卖票
    @Override
    public void run() {
        // 使用死循环,让卖票操作重复执行
        while(true) {
            // 先判断票是否存在
            if(ticket > 0) {
                System.out.println(Thread.currentThread().getName()+ "-->正在卖" + ticket + "张票");
                ticket -- ;
            }
        }
    }
}
/*
    模拟卖票案例
    创建3个线程,同时开启,对共享的票进行出售
 */
public class Demo01Ticket {
    public static void main(String[] args) {
        // 创建Runnable接口的实现类对象
        RunnableImpl run = new RunnableImpl();
        // 创建3个Thread对象
        Thread t0 = new Thread(run);
        Thread t1 = new Thread(run);
        Thread t2 = new Thread(run);

        // 调用start方法开启多线程
        t0.start();
        t1.start();
        t2.start();
    }
}
(3)线程安全问题产生的原理

以卖票程序为例,三个线程同时执行到了“正在卖第“+ticket+”张票“,会出现卖同一张票和不存在的票等问题。原因就是三个线程在抢夺cpu的执行权时,还未等到当前线程卖完票,其他线程就又进行了卖票操作。
在这里插入图片描述

(4)解决线程安全问题的方法
  • 同步代码块

    synchronized关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。

    注意:

    • 通过代码块中的锁对象,可以使用任意的对象
    • 必须要保证多个线程使用的锁对象是同一个
    • 锁对象作用:把同步的代码块锁住,只让一个线程在同步代码块中执行

    代码实现

    public class RunnableImpl implements Runnable {
        // 定义一个多线程共享的票源
        private int ticket = 100;
        // 创建一个锁对象
        Object obj = new Object();
        // 设置线程任务:卖票
        @Override
        public void run() {
            // 使用死循环,让卖票操作重复执行
            while (true) {
                // 同步代码块
                synchronized (obj) {
                    // 先判断是否存在
                    if(ticket > 0) {
                        // 提高安全问题出现的概率,让程序睡眠
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "-->正在卖" + ticket + "张票");
                        ticket -- ;
                    }
                }
            }
        }
    }
    
    public class Demo01Ticket {
        public static void main(String[] args) {
            // 创建Runnable接口的实现类对象
            RunnableImpl run = new RunnableImpl();
            // 创建3个Thread对象
            Thread t0 = new Thread(run);
            Thread t1 = new Thread(run);
            Thread t2 = new Thread(run);
            // 调用start方法开启多线程
            t0.start();
            t1.start();
            t2.start();
        }
    }
    

    同步技术的原理
    上述程序使用了一个锁对象,这个锁对象叫同步锁,也叫对象锁,也叫对象监视器。3个线程一起抢夺CPU的执行权,谁抢到了谁执行run方法进行卖票。t0抢到了cpu的执行权,执行run方法,遇到synchronized代码块。这是t0会检查synchronized代码块是否有锁对象,发现有,就会获取到锁对象,进入到同步中执行。t1抢到了cpu的执行权,执行run方法,遇到synchronnized代码块,这时t1会检查synchronized代码块是否有锁对象,发现没有,t1就会进入到阻塞状态,会一直等待t0线程归还锁对象。直到t0线程执行完同步中的代码,才会把锁对象归还给同步代码块,t1才能获取到锁对象进入到同步中执行。

    同步技术的优缺点

    ​ 同步保证了只有一个线程在同步中执行共享数据,保证了安全。但是程序频繁的判断锁,获取锁,释放锁,程序的效率会降低。

    总结

    同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁进不去同步代码

  • 同步方法

    使用synchronized修饰的方法,就叫做同步方法,保证A线程执行改方法的时候,其他线程只能在方法外等着。

    public synchronized void method(){可能会产生线程安全问题的代码}

    使用同步方法时的同步锁是谁?

    • 对于非静态方法,同步锁就是this

    • 对于静态方法,我们使用当前方法所在类的字节码对象(类名.class)作为同步锁(反射机制)

    代码实现

    /*
        卖票案例出现了线程安全问题
        卖出了不存在的票和重复的票
    
        解决线程安全问题的第二种方案,使用同步方法
        使用步骤:
        1、把访问了共享数据的代码抽取出来,放到一个方法中
        2、在方法上添加synchronized修饰符
        格式:
            定义方法的格式
        修饰符 synchronized 返回值类型 方法名(参数列表) {
            可能会出现线程安全问题的代码(访问了共享数据的代码)
        }
     */
    public class RunnableImpl implements Runnable{
        // 定义一个多线程共享的票源
        private static int ticket = 1000;
        // 设置线程任务:卖票
        @Override
        public void run() {
            // 打印调用run方法的锁对象
            System.out.println("this:"+ this);
            while (true){
                payTicketStatic();
            }
        }
        /*
            静态的同步方法
            锁对象是谁?
            不能是this
            this是创建对象之后产生的,静态方法有限于对象
            静态方法的锁对象是本类的class属性 --> class文件对象(反射)
    
         */
        public static /*synchronized*/ void payTicketStatic(){
            synchronized (RunnableImpl.class) {
                // 先判断是否存在
                if (ticket > 0) {
                    // 提高安全问题出现的概率,让程序睡眠
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "-->正在卖" + ticket + "张票");
                    ticket--;
                }
            }
        }
        /*
            定义一个同步方法
            同步方法也会把方法内部的代码锁住
            只让一个线程执行
            同步方法的锁对象是谁
            就是实现类对象 new RunnableImpl()
            也就是this
        */
        public /*synchronized*/ void payTicket(){
            synchronized (this) {
                // 先判断是否存在
                if (ticket > 0) {
                    // 提高安全问题出现的概率,让程序睡眠
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "-->正在卖" + ticket + "张票");
                    ticket--;
                }
            }
        }
    
    }
    
  • Lock锁

    java.util.concurrent.locks.Lock接口提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。

    Lock接口中的方法:

    void lock()获取锁

    void unlock()释放锁

    java.util.concurrent.locks.ReentrantLock implements LockLock接口的实现类,使用方法及步骤:

    • 在成员位置创建一个ReentrantLock对象
    • 在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁
    • 在可能出现安全问题的代码后调用Lock接口中的方法unlock释放锁

    代码实现

    public class Demo01Ticket {
        public static void main(String[] args) {
            // 创建Runnable接口的实现类对象
            RunnableImpl run = new RunnableImpl();
            // 打印对象run
            System.out.println("run:" + run);
            // 创建3个Thread对象
            Thread t0 = new Thread(run);
            Thread t1 = new Thread(run);
            Thread t2 = new Thread(run);
            // 调用start方法开启多线程
            t0.start();
            t1.start();
            t2.start();
        }
    }
    
    public class RunnableImpl implements Runnable {
        // 定义一个多线程共享的票源
        private int ticket = 1000;
    
        // 1、在成员位置创建一个ReentrantLock对象
        Lock l = new ReentrantLock();
    
        // 设置线程任务:卖票
        @Override
        public void run() {
    
            // 让卖票操作重复执行
            while (ticket > 1) {
                // 2.在可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
                l.lock();
                // 提高安全问题出现的概率,让程序睡眠
                try {
                    Thread.sleep(10);
                    // 票存在,卖票
                    System.out.println(Thread.currentThread().getName() + "-->正在卖" + ticket + "张票");
                    ticket -- ;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //3.在可能会出现安全问题的代码后调用Locke接口中的方法unlock释放锁
                    l.unlock(); // 无论程序是否异常,都会把锁释放
                }
            }
    
        }
    
6、等待唤醒机制
(1)线程状态

新建状态 new:至今尚未启动的线程处于这种状态
阻塞状态 Blocked:具有cpu的执行资格,等待cpu控线时执行
运行状态 Runnable:正在java虚拟机中执行的线程处于这种状态
休眠(睡眠)状态 Timed_Waiting:放弃cpu的执行资格,cpu空闲,也不执行
无限(永久)等待状态 Waiting:无限期的等待另一个线程来执行某一特定操作
死亡状态 Terminated :已退出的线程处于这种状态

进入到TimeWaiting状态有两种方式

  • 使用sleep(long m) 方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态
  • 使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify方法唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态

唤醒的方法

void notify()唤醒在此对象监视器上等待的单个线程

void notifyAll()唤醒在此对象监视器上等待的所有线程

(2)线程间通信

**概念:**多个线程在处理同一个资源,但处理的动作(线程的任务)却不相同。比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费,那么线程A与线程B之间就存在线程通信问题。

(3)Object类中wait带参方法和notify方法

void wait() 在其他线程调用此对象的notify()方法或notifyAll()方法前,当前线程处于等待状态。

void notify()唤醒在此对象监视器上的单个线程,会继续执行wait方法之后的代码

(4)代码实现
public class Demo01WaitAndNotify {
    public static void main(String[] args) {
        // 创建锁对象,保证唯一
        Object obj = new Object();
        // 创建一个顾客线程(消费者)
        new Thread() {
            @Override
            public void run() {
                // 一直等着卖包子
                while (true) {
                    // 保证等待和唤醒的线程只有一个在执行,需要用到同步技术
                    synchronized (obj) {
                        System.out.println("顾客1告知老板要的包子的种类和数量");
                        // 调用wait方法,放弃cpu的执行,进入到WAITING状态(无限等待)
                        try {
                            obj.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        // 唤醒之后执行的代码
                        System.out.println("包子已经做好了,顾客1开吃");
                    }
                }
            }
        }.start();
        // 创建一个老板线程(生产者)
        new Thread() {
            @Override
            public void run() {
                // 一直在做包子
                while(true) {
                    // 花了5秒做包子
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //  保证等待和唤醒的线程只有一个执行,需要使用同步技术
                    synchronized (obj) {
                        System.out.println("老板5秒钟之后做好包子,告知顾客1,可以吃包子了");
                        // 做好包子后,调用notify方法,唤醒顾客吃包子
                        obj.notify();
                    }
                }
            }
        }.start();
    }
}
(5)等待唤醒机制综合案例

包子铺(生产者)、吃货(消费者)共同操作包子。

/*
    资源类:包子类
    设置包子的属性
        皮
        线
        包子的状态:有true,没有false
 */
public class BaoZi {
    // 皮
    String pi;
    // 馅
    String xian;
    // 包子的状态,有true,没有false,设置初始值为false没有包子
    Boolean flag = false;
}
/*
    生产者(包子铺)类是一个线程类,可以继承Thread
    设置线程任务(run):生产包子
    对包子的状态进行判断
    true:
        有包子
        包子铺调用wait()方法进入等待状态
    false:没有包子
        包子铺生产包子
        增加一些趣味性:交替生产两种包子
            有两种状态(i%2== 0)
        包子铺生产好了包子
        修改包子的状态为true 有
        唤醒吃货线程,让吃货线程吃包子
    注意:
        包子铺线程和包子线程关系--> 通信(互斥)
        必须同时同步技术保证两个线程只能有一个再执行
        锁对象必须保证唯一,可以把包子对象作为锁对象
        包子铺类和吃货的类就需要把包子对象作为参数传递进来
            1、需要在成员位置创建一个包子变量
            2、使用带参数构造方法,为这个包子变量赋值

 */
public class BaoZiPu extends Thread{
    // 1、在成员变量位置创建一个包子变量
    private BaoZi bz;
    // 2、使用带参数的构造方法为这个包子赋值
    public BaoZiPu(BaoZi bz) {
        this.bz = bz;
    }

    // 设置线程任务(run):生产包子
    @Override
    public void run() {
        // 定义一个变量
        int count = 0;

        while (true){
            // 必须使用同步技术保证两个线程只有一个在执行
            synchronized (bz) {
                // 对包子的状态进行判断
                if( bz.flag == true ) {
                    try {
                        // 调用wait方法进入等待状态
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 被唤醒之后执行,包子铺生产包子
                // 增加一些趣味性:交替生产两种包子
                if( count % 2 == 0) {
                    // 生产 薄皮三鲜馅的包子
                    bz.pi = "薄皮";
                    bz.xian = "三鲜馅";
                } else {
                    // 生产 冰皮猪肉大葱馅的包子
                    bz.pi = "冰皮";
                    bz.xian = "猪肉大葱";
                }
                count ++;
                System.out.println("包子铺正在生产:" + bz.pi + bz.xian+"的包子!");

                // 生产包子需要三秒钟
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 包子铺生产好了包子之后
                // 修改包子的状态为true
                bz.flag = true;
                // 唤醒吃货线程,让吃货线程吃包子
                bz.notify();//这个唤醒是不是唤醒的包子铺线程自己
                System.out.println("包子铺已经生产好了"+ bz.pi + bz.xian +"的包子,吃货可以来吃了");
            }
        }
    }
}
/*
    消费者(吃货)类:是一个线程类,可以继承Thread
    设置线程任务(run):吃包子
    对包子的状态进行判断
    false:没有包子
        吃货调用wait方法进入等待状态
    true:有包子
        吃货吃包子
        吃货吃完包子
        修改包子的状态为false没有
        吃货唤醒包子铺线程,生产包子
 */
public class ChiHuo extends Thread {
    //1、需要在成员位置创建一个包子变量
    private BaoZi bz;

    //2、使用带参数的构造方法,为这个包子变量赋值
    public ChiHuo(BaoZi bz) {
        this.bz = bz;
    }

    //3、设置线程任务(run):吃包子
    @Override
    public void run() {
        //使用死循环,让吃货一直吃包子
        while (true) {
            // 必须使用同步技术保证两个线程只能有一个再执行
            synchronized (bz) {
                if (bz.flag == false) {
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                // 被唤醒之后执行的代码,吃包子
                System.out.println("吃货正在吃:" + bz.pi + bz.xian + "的包子");
                //吃货吃完包子,修改包子的状态为false
                bz.flag = false;
                // 吃货唤醒包子铺线程,生产包子
                bz.notify();
                System.out.println("吃货已经把:" + bz.pi + bz.xian + "的包子吃完了,包子铺开始生产包子");
                System.out.println("-----------------------------------------");
            }
        }
    }
}
/*
    测试类:
    包含main方法,程序执行的入口,启动程序
    创建包子对象
    创建包子铺线程,开启,生产包子
    创建吃货线程,开启,吃包子
 */
public class Demo {
    public static void main(String[] args) {
        //创建包子对象;
        BaoZi bz = new BaoZi();
        // 创建包子铺线程,开启,生产包子
        new BaoZiPu(bz).start();
        // 创建吃货线程,开启,吃包子
        new ChiHuo(bz).start();
    }
}
7、线程池
(1)概念和原理

线程池:一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

java.util.concurrent.Executors 线程池的工厂类,用来生成线程池

Executors类中的静态方法:

static ExecutorService newFixedThreadPool(int nThreads)创建一个可复用固定线程数的线程池

参数:int nThreads创建线程池中包含的线程数量

返回值:ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收(面向接口编程)

java.util.concurrent.ExecutorService线程池接口

  • submit(Runnable task)提交了一个Runnable任务用于执行,用来从线程池汇总获取线程,调用run方法,执行线程任务。
  • void shutdown()关闭/销毁线程池的方法

线程池的使用步骤:

  • 使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
  • 创建一个类,实现Runnable接口,重写run方法,设置线程任务
  • 调用ExecutorService中的方法submit,传递线程任务(实现开启线程类),执行run方法
  • 调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
(2)代码实现
public class Demo01ThreadPool {
    public static void main(String[] args) {
        // 1、使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
        ExecutorService es = Executors.newFixedThreadPool(2);
        // 3、调用ExecutorService中的方法submit,传递线程任务(实现开启线类),程,执行run方法
        es.submit(new RunnableImpl());
        es.submit(new RunnableImpl());
        // 线程池会一直开启,试用完了之后会归还给线程池,可以拿出来继续使用
        es.shutdown();

        es.submit(new RunnableImpl());// 线程池销毁了,就不能获取线程使用了
    }
}
public class RunnableImpl implements Runnable{
    //2、创建一个类,实现Runnable接口,重写run方法,设置线程任务
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"创建了一个新线程");
    }
}

方法,设置线程任务

  • 调用ExecutorService中的方法submit,传递线程任务(实现开启线程类),执行run方法
  • 调用ExecutorService中的方法shutdown销毁线程池(不建议执行)
(2)代码实现
public class Demo01ThreadPool {
    public static void main(String[] args) {
        // 1、使用线程池的工厂类Executors里边提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池
        ExecutorService es = Executors.newFixedThreadPool(2);
        // 3、调用ExecutorService中的方法submit,传递线程任务(实现开启线类),程,执行run方法
        es.submit(new RunnableImpl());
        es.submit(new RunnableImpl());
        // 线程池会一直开启,试用完了之后会归还给线程池,可以拿出来继续使用
        es.shutdown();

        es.submit(new RunnableImpl());// 线程池销毁了,就不能获取线程使用了
    }
}
public class RunnableImpl implements Runnable{
    //2、创建一个类,实现Runnable接口,重写run方法,设置线程任务
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"创建了一个新线程");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值