day13【线程池、死锁、线程状态、等待与唤醒】synchronized关键字、线程池、死锁、线程状态、等待唤醒机制、定时器Timer

day13【线程池、死锁、线程状态、等待与唤醒】

反馈和复习
1.课上笔记错别字很多,注意一下
2.昨天基本没问题,就是搞混了
3.单词不会读(百度)  
4.课间放歌太好听    

1.synchronized关键字
    a.作用: 控制多行代码的原子性
    b.用法:
			同步代码块:
			synchronized(锁对象){
             	需要同步的代码(需要保证原子性的代码)   
            }
			同步方法:
			public synchronized void 方法名(){
                需要同步的代码(需要保证原子性的代码)   
            }
			Lock锁:
			Lock lock = new ReentrantLock();
			lock.lock();
				需要同步的代码(需要保证原子性的代码)   
    		lock.unlock();
2.各种高并发情况下使用的线程安全的类  
    ArrayList线程不安全-->CopyOnWriteArrayList线程安全
    HashSet线程不安全 --> CopyOnWriteArraySet线程安全
    HashMap线程不安全--> HashTable(全局锁,性能较低),线程安全
						ConcurrentHashMap(局部锁+CAS,性能较高)线程安全
    
    Semaphore: 控制线程最多的并发数量
	CountDownLatch: 可以运行一个线程等待另外一个线程执行完毕后再继续执行
	CyclicBarrier: 可以让多个线程到达某种条件之后,再执行其他任务(五个人都到了,再开会)
    Exchanger: 用于两个线程之间数据交换    
今日内容
1.线程池【重点】
2.死锁【了解】
3.线程的状态【重点也是难点,也是面试中经常会问的】
4.定时器【重点,一共有四种不同的定时器任务】  

第一章 线程池

5.1 线程池的思想
我们需要使用线程时,我们会临时创建一个线程,然后启动,而线程的创建,以及线程使用完毕之后的销毁,都是需要消耗性能的.
思考: 能不能事先把线程对象创建好,然后需要用到时直接把创建好的线程拿过来使用,
		使用完毕,重新归还,等待下次继续使用???! 其实这就是线程池的思想      
5.2 线程池介绍
线程池: 其实就是一个保存多个线程对象的容器,其中的线程可以反复使用,节省了创建和销毁的所消耗性能
5.3 线程池的使用
线程池的顶层接口:
	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<T> submit(Callable<T> task);向线程池中提交有返回值的任务,返回Future类型,
												表示返回了封装线程执行完毕之后结果的那个对象                                                      
5.4 线程池的练习
public class TestThreadPoolDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //1.创建一个线程池,使用多态接收
        ExecutorService service = Executors.newFixedThreadPool(3);

        //2.向线程池中提交 无返回值 任务
//        for (int i = 0; i < 10; i++) {
//            service.submit(new Runnable() {
//                @Override
//                public void run() {
//                    System.out.println(Thread.currentThread().getName()+"执行了....");//pool-1-thread-1
//                }
//            });
//        }
        //3.向线程池中提交 有返回值 任务
        //提交
        Future<Integer> future = service.submit(new Callable<Integer>(){
            @Override
            public Integer call() {
                //求1-100的和任务
                int sum = 0;
                for (int i = 1; i < 101; i++) {
                    sum+=i;
                }
                return sum;
            }
        });

        //从future中取出结果
        Integer result = future.get();// 因为方法具有阻塞功能,会等待任务直接完毕之后再返回结果
        System.out.println("result = " + result);


        //如果想要整个进程停止
        //那么需要关闭线程池
        service.shutdown();
    }
}

第二章 死锁

6.1 什么是死锁
在多线程中有多把锁,最后导致所有都在等待,造成的现象称为死锁
6.2 产生死锁的条件
a.至少有2个线程
b.至少有2个锁对象
c.必须有synchronized的嵌套    
6.3 死锁演示
public class TestDeadLock {
    public static void main(String[] args) {
        //1.先创建2把锁对象
        Object obj1 = new Object();
        Object obj2 = new Object();

        //2.再创建2个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                //3.必须有synchronized嵌套
                while (true){
                    synchronized (obj1){
                        System.out.println("线程1抢到了obj1,还需要抢obj2..");
                        synchronized (obj2){
                            System.out.println("线程1抢到了obj2,那么可以执行了...");
                            System.out.println(Thread.currentThread().getName()+"执行了..");
                        }
                    }
                }

            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                //4.必须有synchronized嵌套
                while (true){
                    synchronized (obj2){
                        System.out.println("线程2抢到obj2,还需要抢obj1...");
                        synchronized (obj1){
                            System.out.println("线程2抢到obj1,那么可以执行了...");
                            System.out.println(Thread.currentThread().getName()+"执行了....");
                        }
                    }
                }

            }
        }).start();
    }
}

注意:如果出现了死锁怎么办??
    	无解!! 我们只能事先尽量避免死锁

第三章 线程的状态【重点,面试常问】

1.线程的六种状态
  • 新建状态(New)

    刚刚创建的且未调用start方法的线程
    
    
  • 可运行状态(Runnable)

    处于新建状态的线程调用了start方法之后
    注意:只有新建状态的线程才能调用start()    
    
    
  • 受(锁)阻塞状态(Blocked)

    线程运行的过程中遇到了同步方法,同步代码块,Lock锁,但是锁已经被其他线程持有了
    
    
  • 限时等待状态(Timed_waiting)

    线程执行到代码Thread.sleep(毫秒值),线程就处于限时等待状态
    
    
  • 无限等待状态(Waiting)

    • 线程如何进入Waiting(无线等待状态)

      a.该线程必须先持有锁对象
      b.调用锁对象的wait方法,进入无限等待
      c.进入无限等待之前,会自动释放锁对象    
      
      
    • 其他线程如何唤醒Waiting状态的线程

      a.其他线程先持有锁对象(就是进入无限等待线程释放的那个锁对象)
      b.调用锁对象的notify方法,唤醒无限等待的线程
      c.被唤醒的无限等待线程,先进入锁阻塞,直到再次持有锁对象才能进入可运行状态    
      
      
  • 消亡状态(Terminated)

    当线程的任务执行完毕,此时线程处于消亡状态
    注意:处于消亡状态的线程,不能再调用start方法起死回生!    
    
    

    在这里插入图片描述

第四章 等待唤醒机制

1.等待唤醒机制(Wait和Notify)
public class TestWaitingDemo {
    public static void main(String[] args) throws InterruptedException {
        //如何让一个线程进入 无限等待
        //1.创建一把锁对象
        Object obj = new Object();

        //2.创建一个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (obj){
                    //1.进入这个代码块,说明线程A抢到锁了
                    System.out.println("线程A持有锁对象obj...");
                    //2.进入无限等待
                    System.out.println("线程A进入了无限等待了...");
                    try {
                        obj.wait(); //进入无限等待之前会自动释放锁对象
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //3.醒来后会继续执行
                    System.out.println("线程A从无限等待中醒来了...");
                }
            }
        }).start();

        Thread.sleep(2000);
        //3.再来一个线程,负责唤醒线程A
        new Thread(new Runnable() {
            @Override
            public void run() {
                //1.持有相同的锁对象
                synchronized (obj){
                    System.out.println("线程B持有锁对象obj...");
                    System.out.println("线程B唤醒了线程A....");
                    //2.调用锁对象的notify方法
                    obj.notify();
                    //3.线程执行一下任务
                    for (int i = 0; i < 10; i++) {
                        System.out.println(i);
                    }
                }
            }
        }).start();
    }
}

注意:
	a.只有线程进入了无限等待,其他线程调用锁对象.notify()才有作用,否则也可以调用,但是没有任何作用(不会报错)
    b.锁对象.notify方法只能唤醒一个线程,具体是哪一个是随机的  
        
    c.锁对象.notifyAll方法可以唤醒多个线程,谁抢到锁谁执行     

2.生产者与消费者问题(代码演示)
  • 需求介绍
需求:
	生产者消费者案例
    需要两个线程: 线程1包子铺线程,负责生产包子,线程2吃货线程,负责吃包子
        如果没有包子,那么吃货线程等待,包子铺线程执行(做包子),做完之后唤醒吃货线程
        如果有包子,那么包子铺线程等待,吃货线程执行(吃包子),吃饭之后唤醒包子铺线程

  • 画图分析

在这里插入图片描述

  • 代码实现

    public class TestDemo {
        public static void main(String[] args) {
            //1.创建一个集合
            ArrayList<String> arr = new ArrayList<String>();
            arr.add("包子");
            //2.创建一个锁
            Object obj = new Object();
            //3.线程1:包子铺线程
            Thread t1 = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        //a.同步代码块
                        synchronized (obj){
                            //b.判断
                            if (arr.size() > 0) {
                                //c.有包子,那么无限等待
                                try {
                                    System.out.println("包子铺发现有包子,进入无限等待...");
                                    obj.wait();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                            //d.没有包子,做包子
                            System.out.println("包子铺做了一个包子....");
                            arr.add("包子");
                            //e.通知吃货线程
                            System.out.println("包子铺做完包子,唤醒吃货...");
                            obj.notify();
                        }
                    }
                }
            });
    
            //4.线程2:吃货线程
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        //a.同步代码块
                        synchronized (obj){
                            //b.判断
                            if (arr.size() == 0) {
                                try {
                                    System.out.println("吃货发现没有包子,进入无限等待....");
                                    obj.wait();
                                } catch (InterruptedException e) {
                                    e.printStackTrace();
                                }
                            }
                            //c.有包子,吃包子
                            System.out.println("吃货吃了包子...");
                            arr.remove(0);
                            //d.通知包子铺做
                            System.out.println("吃货吃完包子,唤醒包子铺...");
                            obj.notify();
                        }
                    }
                }
            });
            //5.启动
            t1.start();
            t2.start();
        }
    }
    
    

第五章 定时器

5.1 什么是定时器
可以让某个线程在某个时间做指定的任务,或者某个时间以后指定的时间间隔中反复做某个任务!!

5.2 定时器Timer的使用
  • 构造方法

    public 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);//在指定的时间点第一次执行任务,继续周期性执行任务,周期的时间间隔period
    
    
  • 案例演示

public class TestTimer {
    public static void main(String[] args) {
        //1.创建一个定时器
        Timer timer = new Timer();
        //2.任务1
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("任务1执行...");
            }
        },2000);

        //3.任务2
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("任务2执行...");
            }
        },2000,1000);

        //4.任务3
        Calendar cc = Calendar.getInstance();
        cc.add(Calendar.SECOND,5);

        Date date = cc.getTime();

        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("任务3执行了...");
            }
        },date);

        //5.任务4
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("任务4执行...");
            }
        },date,1000);
    }
}

总结
1.线程池【理解】
    a.怎么创建???
    	ExecutorService service = Executors.newFixedThreadPool(int 线程个数);
	b.提交任务
        service.submit(Runnable 任务); //提交无返回值任务
        Future<T> future = service.submit(Callable<T> 任务);//提交有返回值任务
		通过 future.get() 该方法会阻塞,直到线程执行完毕,返回结果
    c.关闭线程池
        service.shutDown();    
		   
2.死锁【了解】
     a.多个线程
     b.多把锁
     c.嵌套获取锁
     死锁只能尽量避免
    
3.线程的状态(等待和唤醒机制) 【掌握】   
    a.NEW(新建状态)
    b.RUNNABLE(可运行状态)
    c.TERMINATED(消亡状态)
    d.BLOCKED(锁阻塞状态)
    e.TIMED_WAITING(限时等待状态)
    f.WAITING(无限等待状态)
    
    怎么进入WAITING状态??
    	a.当前线程获取锁对象
    	b.调用锁对象.wait()方法
    	c.进入WAITING之前自动释放锁对象
    其他线程怎么唤醒WAITING的线程??
    	a.其他线程持有锁对象
    	b.调用锁对象.notify()方法
    	c.WAITING的线程就会醒来,先进入BLOCKED状态,直到再次获取到锁对象
    
    需要练习两个相关案例demo05和demo06
    
4.Timer【理解】  
    四个方法
    public void schedule(TimerTask task, long delay); 

	public void schedule(TimerTask task, long delay, long period);

	public void schedule(TimerTask task, Date time);

	public void schedule(TimerTask task, Date firstTime,long period);

发布了23 篇原创文章 · 获赞 0 · 访问量 1139

猜你喜欢

转载自blog.csdn.net/qq_44845814/article/details/104995348