线程池回顾

时间:2021-07-26作者:klpeng分类:Java技术浏览:105评论:0

线程池

线程池思想

线程池回顾

线程池概述

线程池:是一个存储线程的容器,当我们要使用线程的时候,就可以从线程池中获取一个线程,使用完毕在把线程归还到线程池
java.util.concurrent.Executors:是一个创建线程池的工厂类,专门用来生产线程池,里边的方法都是静态的
     静态方法:
         static ExecutorService newFixedThreadPool(int nThreads)
             创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
         参数:
               int nThreads:创建线程池,包含线程的数量
         返回值:
              ExecutorService:ExecutorService就是一个线程池,是一个接口,返回的是ExecutorService接口的实现类对象
                 可以使用ExecutorService接口来接收这个实现类对象(多态)
java.util.concurrent.ExecutorService:描述线程池的接口
     常用的方法:
         Future<?> submit(Runnable task) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 FutureFuture<T> submit(Callable<T> task) 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
        参数:
             Runnable task:传递Runnable接口的实现类对象(线程任务)==>重写run方法==>run方法没有返回值
             Callable<T> task:传递Callable接口的实现类对象(线程任务)==>重写call方法==>有返回值
        返回值:
            Future<T>:用来接收线程任务的返回值,用来接收call方法的返回值

使用线程池执行Runnable接口的线程任务

/*
    线程池:是一个存储线程的容器,当我们要使用线程的时候,就可以从线程池中获取一个线程,使用完毕在把线程归还到线程池
    java.util.concurrent.Executors:是一个创建线程池的工厂类,专门用来生产线程池,里边的方法都是静态的
        静态方法:
            static ExecutorService newFixedThreadPool(int nThreads)
                创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
            参数:
                  int nThreads:创建线程池,包含线程的数量
            返回值:
                 ExecutorService:ExecutorService就是一个线程池,是一个接口,返回的是ExecutorService接口的实现类对象
                    可以使用ExecutorService接口来接收这个实现类对象(多态)
   java.util.concurrent.ExecutorService:描述线程池的接口
        常用的方法:
            Future<?> submit(Runnable task) 提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
            Future<T> submit(Callable<T> task) 提交一个返回值的任务用于执行,返回一个表示任务的未决结果的 Future。
           参数:
                Runnable task:传递Runnable接口的实现类对象(线程任务)==>重写run方法==>run方法没有返回值
                Callable<T> task:传递Callable接口的实现类对象(线程任务)==>重写call方法==>有返回值
           返回值:
               Future<T>:用来接收线程任务的返回值,用来接收call方法的返回值
   -------------------------------------------------------
   线程池的实现步骤(重点):
        1.使用Executors线程池工厂类中的静态方法newFixedThreadPool创建一个包含指定线程数量的线程池ExecutorService
        2.创建Runnable接口|Callable接口的实现类对象,重写run方法|call方法,设置线程任务
        3.使用线程池ExecutorService中的方法submit,传递线程任务(Runnable接口|Callable接口的实现类对象)
            submit方法会在线程池中获取一个线程,执行线程任务
 */
public class Demo01ThreadPool {
    public static void main(String[] args) {
        //1.使用Executors线程池工厂类中的静态方法newFixedThreadPool创建一个包含指定线程数量的线程池ExecutorService
        ExecutorService es = Executors.newFixedThreadPool(3);//线程池中包含了3个线程

        //3.使用线程池ExecutorService中的方法submit,传递线程任务(Runnable接口|Callable接口的实现类对象)submit方法会在线程池中获取一个线程,执行线程任务
        //new Thread(new RunnableImpl()).start();
        es.submit(new RunnableImpl());//pool-1-thread-2线程正在执行线程任务!
        es.submit(new RunnableImpl());//pool-1-thread-1线程正在执行线程任务!
        es.submit(new RunnableImpl());//pool-1-thread-3线程正在执行线程任务!
        es.submit(new RunnableImpl());//pool-1-thread-2线程正在执行线程任务!

        //匿名内部类
        es.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"线程正在执行线程任务!");
            }
        });

        /*
            线程池中的方法:(了解)
                void shutdown()  用于销毁线程池的方法
            注意:
                线程池一旦被销毁,就不能在使用了,会抛出异常
         */
        es.shutdown();
        es.submit(new RunnableImpl());//RejectedExecutionException

    }
}

//2.创建Runnable接口|Callable接口的实现类对象,重写run方法|call方法,设置线程任务
public class RunnableImpl implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程正在执行线程任务!");
    }
}

使用Callable接口的线程任务

/*
    使用线程池执行Callable接口的线程任务(重点)
    java.util.concurrent.Callable<V>
        返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。
        Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。
        但是 Runnable 接口没有返回结果,Callable接口有返回的结果
    Callable接口的作用:
        可以使用Callable接口中的call方法,用于设置线程任务,而这个线程任务包含了一个返回值
    Callable接口中的方法:
        V call() 计算结果,如果无法计算结果,则抛出一个异常。
        返回值:
            V:返回一个指定泛型的值,接口使用什么泛型,就返回一个什么类型的值
 */
public class Demo02ThreadPoll {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.使用Executors线程池工厂类中的静态方法newFixedThreadPool创建一个包含指定线程数量的线程池ExecutorService
        ExecutorService es = Executors.newFixedThreadPool(3);
        //2.创建Runnable接口|Callable接口的实现类对象,重写run方法|call方法,设置线程任务
        Callable<Integer> callable = new Callable<Integer>(){
            @Override
            public Integer call() throws Exception {
                //返回一个随机的整数
                return new Random().nextInt(10);//产生随机数的范围[0-9]
            }
        };
        //3.使用线程池ExecutorService中的方法submit,传递线程任务(Runnable接口|Callable接口的实现类对象)submit方法会在线程池中获取一个线程,执行线程任务
        Future<Integer> future = es.submit(callable);
        System.out.println(future);//java.util.concurrent.FutureTask@14ae5a5

        /*
            java.util.concurrent.Future<V>接口
                Future 表示异步计算的结果。用来接收call方法的返回值
            Future<V>接口中的方法
                V get() 如有必要,等待计算完成,然后获取其结果。用于获取对象中接收到的call方法的返回值
         */
        Integer v = future.get();
        System.out.println(v);

        Future<Double> f2 = es.submit(new Callable<Double>() {
            @Override
            public Double call() throws Exception {
                //返回一个随机小数[0.0--1.0]
                return Math.random();
            }
        });
        System.out.println(f2.get());
    }
}

死锁

死锁原理

线程池回顾

死锁代码实现

/*
    死锁:两个线程你拿着我的锁,我拿着你的锁,导致两个线程都进不去同步继续执行
    前提:
        必须有同步代码块的嵌套
        必须有两个锁对象
        必须有连个线程在执行
 */
public class RunnableImpl implements Runnable{
    //创建两个锁对象
    private String lockA = "A锁";
    private String lockB = "B锁";
    //定义一个变量,用于两个线程交叉执行,一人执行一次
    int a = 0;
    @Override
    public void run() {
        //定义一个死循环,让线程重复执行
        while (true){
            //判断a是奇数还是偶数
            if(a%2==0){//偶数,让一个线程执行
                //同步代码块的嵌套
                synchronized (lockA){
                    System.out.println(Thread.currentThread().getName()+"into if lockA...");
                    synchronized (lockB){
                        System.out.println(Thread.currentThread().getName()+"into if lockB...");
                    }
                }
            }else {//奇数,让另外一个线程执行
                //同步代码块的嵌套
                synchronized (lockB){
                    System.out.println(Thread.currentThread().getName()+"into else lockB...");
                    synchronized (lockA){
                        System.out.println(Thread.currentThread().getName()+"into else lockA...");
                    }
                }
            }
            a++;
        }
    }
}

//测试类
public class Demo01DeadLock {
    public static void main(String[] args) {
        //创建两个线程,同时执行线程任务
        RunnableImpl r = new RunnableImpl();
        new Thread(r).start();
        new Thread(r).start();
    }
}

执行结果:

Thread-1into if lockA...
Thread-1into if lockB...
Thread-1into else lockB...
Thread-1into else lockA...
Thread-1into if lockA...
Thread-0into else lockB...

注意:平时写代码尽量避免死锁出现

线程状态

线程池回顾

Object类中等待与唤醒方法

Object类中的方法:
void wait() 
   在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。 
void notify() 
   唤醒在此对象监视器(同步锁,对象锁)上等待的单个线程。 
void notifyAll() 
   唤醒在此对象监视器上等待的所有线程。 
注意:
	wait()notify()方法一般都是在同步代码块中使用
	一般都是使用锁对象调用wait()notify()方法(多个线程使用的是同一个锁对象)
		锁对象-->wait()-->Thread-0线程-->等待
   		锁对象-->notify()-->唤醒在锁对象上等待的线程-->唤醒Thread-0

等待与唤醒案例(重点)

线程池回顾

代码实现

/*
    包子类:资源类
	属性:皮,馅,状态(有,没有)
 */
public class BaoZi {
    //皮
    String pi;
    //馅
    String xian;
    //包子的状态:初始值为false
    boolean flag = false;
}


/*
    包子铺类:是一个线程类
    线程任务:做包子
    对包子的状态进行判断
      true:有包子
        包子铺等待  wait();
      false:没有包子
        包子铺线程做包子
        打印做x皮x馅的包子
        花3秒钟做包子
        3秒钟之后做好了包子
        修改包子的状态为有
        唤醒吃货线程吃包子
   注意:
    必须保证包子铺线程和吃货线程只能有一个在执行
    可以使用同步代码块,需要使用一个锁对象,必须保证两个线程使用同一个锁对象
    可以使用 包子对象作为锁对象
    定义一个包子变量,使用能构造方法为变量赋值
 */
//包子铺类:是一个线程类
public class BaoZiPu extends Thread{
    //定义一个包子变量,使用能构造方法为变量赋值
    private BaoZi bz;

    public BaoZiPu(BaoZi bz) {
        this.bz = bz;
    }

    //线程任务:做包子
    @Override
    public void run() {
        //增加一个死循环,让包子铺一直做包子
        while (true){
            //同步代码块
            synchronized (bz){
                //对包子的状态进行判断
                if(bz.flag==true){
                    //true:有包子,包子铺等待  wait();
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //包子铺线程被唤醒之后,会继续执行wait之后的代码
                //false:没有包子,包子铺线程做包子
                //打印做x皮x馅的包子
                bz.pi = "薄皮";
                bz.xian = "牛肉大葱陷";
                System.out.println("包子铺线程正在做"+bz.pi+bz.xian+"的包子!");
                //花3秒钟做包子
                System.out.println("包子铺做包子的需要3秒钟时间!");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //3秒钟之后做好了包子,修改包子的状态为有
                bz.flag = true;
                //唤醒吃货线程吃包子
                bz.notify();//唤醒包子对象上等待的吃货线程
                System.out.println("包子铺线程已经做好了"+bz.pi+bz.xian+"的包子,吃货线程赶紧来吃包子吧");
            }
        }
    }
}

/*
    吃货类:是一个线程类
    线程任务:吃包子
    对包子的状态进行判断
      false:没有包子
         吃货线程等待 包子对象.wait():
      true:有包子
         吃货线程吃包子
         打印吃x皮x馅的包子
         花1秒钟吃包子
         1秒钟之后吃完包子
          修改包子的状态为没有
          唤醒包子铺线程做包子
   注意:
    必须保证包子铺线程和吃货线程只能有一个在执行
    可以使用同步代码块,需要使用一个锁对象,必须保证两个线程使用同一个锁对象
    可以使用 包子对象作为锁对象
    定义一个包子变量,使用能构造方法为变量赋值
 */
/货类:是一个线程类
public class ChiHuo implements Runnable{
    //定义一个包子变量,使用能构造方法为变量赋值
    private BaoZi bz;

    public ChiHuo(BaoZi bz) {
        this.bz = bz;
    }

    //线程任务:吃包子
    @Override
    public void run() {
        //让吃货线程一直吃包子,增加一个死循环
        while (true){
            //同步代码块
            synchronized (bz){
                //对包子的状态进行判断
                if(bz.flag==false){
                    //false:没有包子,吃货线程等待 包子对象.wait():
                    try {
                        bz.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                /货线程被唤醒之后,会继续执行wait之后的代码
                //true:有包子,吃货线程吃包子
                //打印吃x皮x馅的包子
                System.out.println("吃货正在吃"+bz.pi+bz.xian+"的包子!");
                //花1秒钟吃包子
                System.out.println("吃货吃包子需要1秒钟!");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //1秒钟之后吃完包子,修改包子的状态为没有
                bz.flag= false;
                //唤醒包子铺线程做包子
                bz.notify();//唤醒包子对象上等待的包子铺线程
                System.out.println("吃货线程已经吃完"+bz.pi+bz.xian+"的包子,包子铺线程感觉做包子把!");
                System.out.println("------------------------------------------------------------------");
            }
        }
    }
}


/*
    测试类
	创建包子对象,创建2个线程,一个线程做包子,一个线程吃包子
 */
public class Demo01WaitAndNotify {
    public static void main(String[] args) {
        //创建包子对象
        BaoZi bz = new BaoZi();
        //创建2个线程,一个线程做包子,一个线程吃包子
        new BaoZiPu(bz).start();
        new Thread(new ChiHuo(bz)).start();
    }
}

定时器(重点)

定时器,可以设置线程在某个时间执行某件事情,或者某个时间开始,每间隔指定的时间反复的做某件事情

/*
    java.util.Timer类:
        一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
    构造方法:
        Timer() 创建一个新计时器。
    成员方法:
        void cancel() 终止此计时器,丢弃所有当前已安排的任务。
            注意,在此计时器调用的计时器任务的 run 方法内调用此方法,就可以绝对确保正在执行的任务是此计时器所执行的最后一个任务。
        void schedule(TimerTask task, long delay) 安排在指定延迟后执行指定的任务。只执行一次
            参数:
                TimerTask task:所要安排的任务。定时器要执行的任务
                long delay:执行任务前的延迟时间,单位是毫秒。多少毫秒之后执行定时器的任务
        void schedule(TimerTask task, long delay, long period) 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。
            参数:
                TimerTask task:所要安排的任务。定时器要执行的任务
                long delay:执行任务前的延迟时间,单位是毫秒。多少毫秒之后执行定时器的任务
                long period:执行各后续任务之间的时间间隔,单位是毫秒。 定时器开始执行之后,每隔多少毫秒重复执行
        void schedule(TimerTask task, Date time) 安排在指定的时间执行指定的任务。只执行一次
            参数:
                TimerTask task:所要安排的任务。定时器要执行的任务
                Date time:执行任务的时间。 什么日期开始执行任务 2020-02-12 14:46:45
        void schedule(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定延迟执行。
            参数:
                TimerTask task:所要安排的任务。定时器要执行的任务
                firstTime - 首次执行任务的时间。 什么日期和事件开始执行任务 2020-02-12 14:46:45
                period - 执行各后续任务之间的时间间隔,单位是毫秒。定时器开始执行之后,每隔多少毫秒重复执行
    java.util.TimerTask:由 Timer 安排为一次执行或重复执行的任务。
            void run() 此计时器任务要执行的操作。重写run方法设置线程任务
 */
public class Demo01Timer {
    public static void main(String[] args) throws ParseException {
        show04();
    }

    private static void show04() throws ParseException {
        //创建一个反复执行的定时器:2020-02-12 15:02:45开始执行,每隔1秒钟执行1次
        Timer t = new Timer();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse("2020-02-12 15:02:50");
        t.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("哈哈");
            }
        },date,1000);
    }

    private static void show03() throws ParseException {
        //创建一个执行一次的定时器:2020-02-12 15:00:45
        Timer t = new Timer();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse("2020-02-12 15:00:10");
        t.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("哈哈");
                t.cancel();
            }
        },date);
    }

    private static void show02() {
        //创建一个反复执行的定时器,5秒钟之后开始执行,每隔1秒钟执行一次
        Timer t = new Timer();
        t.schedule(new TimerTask() {
            @Override
            public void run() {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                System.out.println(sdf.format(new Date()));
            }
        },5000,1000);
    }

    private static void show01() {
        //创建一个执行一次的定时器,5秒钟之后开始执行
        Timer t = new Timer();
        t.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("c4爆炸了!");
                t.cancel();
            }
        },5000);
    }
}
打赏
文章版权声明:除非注明,否则均为彭超的博客原创文章,转载或复制请以超链接形式并注明出处。
相关推荐

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

猜你喜欢