3/18day13_线程池_死锁_线程的状态_定时器

2020-03-18  本文已影响0人  蹦蹦跶跶的起床啊

复习

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: 用于两个线程之间数据交换  

今日内容

线程池方式

线程池的思想

如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。

线程池的概念

线程池的使用

线程池代码实现

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("我要一个教练");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("教练来了: " + Thread.currentThread().getName());
        System.out.println("教我游泳,教完后,教练回到了游泳池");
    }
}

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 创建线程池对象
        ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
        // 创建Runnable实例对象
        MyRunnable r = new MyRunnable();
 
        //自己创建线程对象的方式
        // Thread t = new Thread(r);
        // t.start(); ---> 调用MyRunnable中的run()
 
        // 从线程池中获取线程对象,然后调用MyRunnable中的run()
        service.submit(r);
        // 再获取个线程对象,调用MyRunnable中的run()
        service.submit(r);
        service.submit(r);
        // 注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。
        // 将使用完的线程又归还到了线程池中
        // 关闭线程池
        //service.shutdown();
    }
}
public class ThreadPoolDemo2 {
    public static void main(String[] args) throws Exception {
        // 创建线程池对象
      ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
 
        // 创建Runnable实例对象
        Callable<Double> c = new Callable<Double>() {
            @Override
            public Double call() throws Exception {
                return Math.random();
            }
        };
 
        // 从线程池中获取线程对象,然后调用Callable中的call()
        Future<Double> f1 = service.submit(c);
        // Futur 调用get() 获取运算结果
        System.out.println(f1.get());
 
        Future<Double> f2 = service.submit(c);
        System.out.println(f2.get());
 
        Future<Double> f3 = service.submit(c);
        System.out.println(f3.get());
    }
}

死锁

死锁概念

在多线程程序中,使用了多把锁,造成线程之间相互等待.程序不往下走了。
应该尽量避免死锁

产生死锁的条件

1.有多把锁 2.有多个线程 3.有同步代码块synchronized嵌套
synchronized 获取不到锁, 就会一直等待

避免死锁

死锁代码演示

public class Demo05 {
    public static void main(String[] args) {
        MyRunnable mr = new MyRunnable();
 
        new Thread(mr).start();
        new Thread(mr).start();
    }
}
 
class MyRunnable implements Runnable {
    Object objA = new Object();
    Object objB = new Object();
 
    /*
    嵌套1 objA
    嵌套1 objB
    嵌套2 objB
    嵌套1 objA
     */
    @Override
    public void run() {
        synchronized (objA) {
            System.out.println("嵌套1 objA");
            synchronized (objB) {// t2, objA, 拿不到B锁,等待
                System.out.println("嵌套1 objB");
            }
        }
 
        synchronized (objB) {
            System.out.println("嵌套2 objB");
            synchronized (objA) {// t1 , objB, 拿不到A锁,等待
                System.out.println("嵌套2 objA");
            }
        }
    }
}

线程的状态[非常重点]

线程的六种状态

等待和唤醒代码实现

Object类的方法
public void wait() : 让当前线程进入到等待状态 此方法必须锁对象调用.此方法包含自动释放锁功能

public class Demo1_wait {
    public static void main(String[] args) throws InterruptedException {
       // 步骤1 : 子线程开启,进入无限等待状态, 没有被唤醒,无法继续运行.
        new Thread(() -> {
            try {
 
                System.out.println("begin wait ....");
                synchronized ("") {
                    "".wait();
                }
                System.out.println("over");
            } catch (Exception e) {
            }
        }).start();
    }

public void notify() :(只能唤醒一个线程) 唤醒当前锁对象上等待状态的线程 此方法必须之前等待的锁对象调用.此方法不包含释放锁对象功能, 需要该线程结束后, 才能释放锁对象给唤醒的线程, 唤醒的线程才能继续执行锁对象内部的代码

public class Demo2_notify {
    public static void main(String[] args) throws InterruptedException {
       // 步骤1 : 子线程开启,进入无限等待状态, 没有被唤醒,无法继续运行.
        new Thread(() -> {
            try {
 
                System.out.println("begin wait ....");
                synchronized ("") {
                    "".wait();//进入无限等待之前,会自动释放锁对象
                }
                System.out.println("over");
            } catch (Exception e) {
            }
        }).start();
 
        //步骤2:  加入如下代码后, 3秒后,会执行notify方法, 唤醒wait中线程.
        Thread.sleep(3000);
        new Thread(() -> {
            try {
                synchronized ("") {
                    System.out.println("唤醒");
                    "".notify();
                }
            } catch (Exception e) {
            }
        }).start();
    }
}

等待和唤醒注意事项

定时器

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

定时器的使用

java.util.Timer类:线程调度任务以供将来在后台线程中执行的功能。任务可以安排一次执行,或者定期重复执行。

public class Test{
    public static void main(String[] args){
       //1.设置一个定时器,2秒后启动,只执行一次
        Timer t = new Timer();
        t.schedule(new TimerTask(){
            @Override
            public void run(){
                for(int i = 10;i >= 0 ; i--){
                    System.out.println("倒数:" + i);
                    try{
                        Thread.sleep(1000);
                    }catch(Exception e){}
                }
                System.out.println("嘭......嘭......");
                //任务执行完毕,终止计时器
                t.cancel();
            }
        },2000);
        
        //2.设置一个定时器,5秒后开始执行,每一秒执行一次
        Timer t2 = new Timer();
        t2.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);
        
        //3.设置一个定时器,在2030年01月01日零时开始执行,每隔24小时执行一次
        Timer t3 = new Timer();
        Calendar c = new GregorianCalendar(2030, 1-1, 1, 0, 0, 0);
        Date d = c.getTime();
        
        t3.schedule(new TimerTask(){
            @Override
            public void run(){
                System.out.println("搜索全盘......");
            }
        },d,1000 * 3600 * 24);
    }
}

今日小结

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);
上一篇 下一篇

猜你喜欢

热点阅读