关于线程池、死锁、线程状态、等待与唤醒的问题,你觉得难吗???

线程池方式【重点】

线程池对的思想

我们需要使用线程时,我们会临时创建一个线程,然后启动,但是这样太消耗时间,所以就有了线程池的思想。

线程池的概念

线程池:存放很多线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象,线程池可以降低资源的消耗,提高响应的速度。

线程池的使用

java中线程池的顶层接口:

java.util.concurrent.Executor;

线程池的子接口:

java.util.concurrent.ExecutorService;

线程池有一个工具类,其作用是帮助我们创建一个线程池对象。

java.util.concurrent.Executors;

工具类中的静态方法:创建一个线程池对象

public static ExecutorService newFixedThreadPool(int nThreads)//用于创建一个具有指定线程个数的线程池对象

如何向线程池中添加任务?
调用ExecutorService接口中规定的方法

public Future<?> submit (Runnable task);//向线程池中添加无返回值的任务
public Future<?> submit (Callable<T> task);//向线程池中添加有返回值的任务,返回Future类型,表示返回了封装线程结束之后结果的对象

线程池的练习

代码实现:
TestThreadPool.java

public class TestThreadPool {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1、创建一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        //2、向线程池中添加无返回值的任务
        //executorService.submit(new Runnable() {
        //    @Override
        //    public void run() {
        //        System.out.println(Thread.currentThread().getName()+"执行了");//获得当前线程的名称【pool-1-thread-1执行了】
        //    }
        //});
        //3、向线程池中添加有返回值的任务,
        Future<Integer> future = executorService.submit(new Callable<Integer>() {
            @Override
            public Integer call() {
                int sum = 0;
                for (int i = 0; i < 101; i++) {
                    sum += i;
                }
                return sum;
            }
        });
        //从future中提取结果
        Integer result = future.get();//get方法具有阻塞功能,会等任务直接完毕后再返回结果
        System.out.println("结果是:" + result);//结果是:5050
        //如果想要整个线程停止,则需要关闭整个线程池
        executorService.shutdown();//关闭整个线程池
    }
}

案例需求:

  1. 创建线程池对象,包含2个线程。从线程池中获取线程对象,然后调用MyRunnable中的run()。
  2. 在MyRunnable实现类中,首先在控制台打印需求,“我需要一个老师”。模拟需要2秒钟时间老师可以过来指导学生,
    并在控制台打印老师的姓名。最后,在控制台打印“教我java,教完后,老师回到了办公室”;
    代码实现:
    MyRunnable.java
public class MyRunnable implements Runnable {
    private String name;

    public MyRunnable(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("我需要一个老师");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name+"教完后,回办公室了");
    }
}

Test.java

public class Test {
    public static void main(String[] args) {
        //创建一个线程池
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        //创建两个线程
        MyRunnable myRunnable1 = new MyRunnable("李老师");
        MyRunnable myRunnable2 = new MyRunnable("刘老师");
        //将两个线程添加到线程池中,并执行
        executorService.submit(myRunnable1);
        executorService.submit(myRunnable2);
        //关闭整个线程池
        executorService.shutdown();
    }
}

死锁

在多线程中有多把锁,最后导致所有的线程都在等待,造成的现象称为死锁。

什么是死锁

产生死锁对的条件

1、至少有两个线程
2、至少有两个锁对象
3、必须有synchronized嵌套

死锁代码

public class DeadLockTest {
    public static void main(String[] args) {
     	//至少有两个锁对象
        Object obj1 = new Object();
        Object obj2 = new Object();
        //至少有两个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                //必须有synchronized嵌套
                synchronized (obj1) {
                    System.out.println("线程1抢到了obj1,还需要抢obj2");
                    synchronized (obj2) {
                        System.out.println("抢到obj2");
                    }
                }
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj2) {
                    System.out.println("线程2抢到了obj2,还需要抢obj1");
                   
                    synchronized (obj1) {
                        System.out.println("抢到obj1");
                    }
                }
            }
        }).start();
    }
}

如果出现了死锁,无解,我们只能事先避免死锁。

线程状态【重点也是难点,面试不可少】

线程状态概述

1、线程的六种状态
线程状态。 线程可以处于以下状态之一:
NEW (新建状态)
尚未启动的线程处于此状态。
RUNNABLE (可运行状态)
处于新建状态的线程调用了start方法之后,线程处于此状态。只有新建状态的线程才能调用start()
BLOCKED (受阻塞/锁阻塞状态)
被阻塞等待监视器锁定的线程处于此状态。
WAITING (无限等待)
正在等待另一个线程执行特定动作的线程处于此状态。
线程如何进入Wating?
1、该线程必须先持有锁对象
2、调用锁对象的wait方法,进入无限等待
3、进入无限等待之前,会自动释放锁对象
其他线程如何唤醒Waiting状态的过程
1、其他线程必须持有锁对象
2、调用锁对象的.nofity()方法
3、waiting的线程就会醒来,先进入blocked状态,直到再次获取到锁对象
TIMED_WAITING (限时等待/休眠)
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。
TERMINATED (退出/消亡状态)
已退出的线程处于此状态。
一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。

睡眠sleep方法

public static void sleep(long time);//让当前线程进入睡眠状态,到毫秒后自动醒来执行

等待和唤醒

public void wait ();//让当前线程进入到等待状态,此方法必须锁对象调用

等待唤醒方案

案例需求:

生产者消费者案例
需求两个线程:线程1包子铺线程,负责生产包子,线程2负责吃货线程,负责吃包子。
如果没有包子,那么吃货线程等待,包子铺线程执行(做包子),做完之后,唤醒吃货线程。
如果有包子,那么包子铺线程等待,吃货线程执行完(吃包子),吃完之后,唤醒包子铺线程。
代码实现:

public class TestDemo {
    public static void main(String[] args) {
        ArrayList<String> arr = new ArrayList<>();
        arr.add("包子");
        Object obj = new Object();
        //包子铺线程
        Thread ti = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (obj) {
                        if (arr.size() > 0) {
                            try {
                                System.out.println("包子铺发现有包子,进入无限等待");
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                        //没有包子,做包子
                        System.out.println("包子做了一个包子");
                        arr.add("包子");
                        //通知吃货线程
                        obj.notify();
                    }
                }
            }
        });
        //吃货线程
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    synchronized (obj) {
                        if (arr.size() == 0) {
                            try {
                                System.out.println("吃货发现没有包子,进入无限等待");
                                obj.wait();
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }

                        //有包子,吃包子
                        System.out.println("吃货吃了一个包子");
                        arr.remove(0);
                        //吃完后,通知吃货线程
                        obj.notify();
                    }
                }
            }
        });
        ti.start();
        t2.start();
    }
}

定时器

定时器概述

可以让某个线程在某个时间做指定任务,或者在某个时间以后指定的时间间隔中反复做某个任务。

定时器Timer类

构造方法:

publc Timer();//构造一个定时器

成员呢方法:

public void schedule(TimerTask task, long delay);//在指定的时间之后执行指定的任务
public void schedule(TimerTask task, long delay,long period);//在指定的时间之后开始周期性的执行指定的任务,周期的时间间隔是period
public void schedule(TimerTask task, Date time);//在指定的时间点执行指定的任务
public void schedule(TimerTask task,Date firstTime,long period);//在指定的时间点第一次执行任务,继续周期性执行任务

代码实现

public class TestTimer {
    public static void main(String[] args) {
        //1、创建一个定时器
        Timer timer = new Timer();
        //任务一
        //timer.schedule(new TimerTask() {
        //    @Override
        //    public void run() {
        //        System.out.println("任务一");
        //    }
        //}, 2000);
        //任务二
        //timer.schedule(new TimerTask() {
        //    @Override
        //    public void run() {
        //        System.out.println("任务二");
        //    }
        //}, 2000, 2000);
        ////任务三
        //Calendar cc = Calendar.getInstance();
        //cc.add(Calendar.SECOND, 10);
        //Date time = cc.getTime();
        //
        //timer.schedule(new TimerTask() {
        //    @Override
        //    public void run() {
        //        System.out.println("任务三");
        //    }
        //}, time);
        //任务四
        Calendar cc = Calendar.getInstance();
        cc.add(Calendar.SECOND, 10);
        Date time = cc.getTime();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("任务四");
            }
        }, time, 1000);
    }
}

小结

关于线程池是面试中的重点,同时也是难点,感觉好抽象,容易搞混。这样的情况下就只能通过多写对应练习即可。
俗话说,熟能生巧嘛。加油!!!

发布了34 篇原创文章 · 获赞 9 · 访问量 1286

猜你喜欢

转载自blog.csdn.net/weixin_43616750/article/details/104936800