24.多线程(等待唤醒机制,volatile,CAS 算法,线程池,定时器,设计模式)

1.线程间的等待唤醒机制

Object 类中
  void wait ()  在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待。
       
 void wait (long timeout) 在其他线程调用此对象的 notify () 方法或 notifyAll () 方法,或者超过指定的时间量前,导致当前线程等待。
 
   void notify () 唤醒在此对象监视器上等待的单个线程,随机唤醒。
        
    void notifyAll ()  唤醒在此对象监视器上等待的所有线程。 
    
notify()需要在同步方法或同步块中调用,即在调用前,线程必须获得该对象的对象级别锁
在调用wait()方法之前,线程必须获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait方法。

wait()和sleep()的区别

Wait和Sleep的区别:
 1.它们最大本质的区别是,Sleep()不释放同步锁,Wait()释放同步锁。
 2.还有用法的上的不同是:Sleep(milliseconds)可以用时间指定来使他自动醒过来,
 如果时间不到你只能调用Interreput()来强行打断;Wait()可以用Notify()直接唤起。
 4.这两个方法来自不同的类分别是Thread和Object
 5.最主要是Sleep方法没有释放锁,而Wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。

SetThread(生产者线程):

//生成者线程
public class SetThread extends Thread {
    private Student student;
    int i = 1;

    public SetThread(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (student) {
                if (student.flag) {
                    //进来了
                    try {
                        //wait(),线程一旦等待,就会释放锁。
                        student.wait(); //让当前的线程等待,在哪里等了,下次被唤醒后就从这里醒来
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //生产资源
                if (i % 2 == 0) {
                    student.name = "张三";
                    student.age = 23;
                } else {
                    student.name = "李四";
                    student.age = 24;
                }
                //通知消费消费线程,进行消费
                student.flag = true; //修改标记
                student.notify(); //唤醒等待的线程,唤醒之后,所有线程都可以再次的争抢时间片
            }
            i++;
        }

    }
}

GetThread(消费者线程):

//消费者线程
public class GetThread extends Thread {
    private Student student;

    public GetThread(Student student) {
        this.student = student;
    }

    @Override
    public void run() {
        while (true){
            synchronized (student){
                if (!student.flag) { //没资源
                    //进来了
                    try {
                        student.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //消费
                System.out.println(student.name + "===" + student.age);
                student.flag=false;
                student.notify();

            }

        }

    }
}

共享资源:

//资源
public class Student {
    public String name;
    public int age;
    public boolean flag=false; // false 表示没有资源,true表示有资源了
}

测试类:

public class MyTest {
    public static void main(String[] args) {
        //生产者线程  SetThread
        //消费者线程 GetThread
        //资源 Student 要共享

        /*
        *  线程安全问题出现的条件:
        * 1. 多线程环境
        * 2. 有共享资源
        * 3. 对共享的资源是否是原子性操作

        * */

        /*
        *
        *  我要的效果是,生成一个,消费一个
        *
        *  作为生成者,生产出了资源,那么有资源了,就等着,你得通知消费线程进行消费
        * 作为消费者,消费了资源,没有资源了,等着,你得通知生产者线程,进行生产。
        *
        * 生产线程和消费线程之间怎么进行通信?
        *
        *  void notify()
          唤醒在此对象监视器上等待的单个线程。
            void notifyAll()
          唤醒在此对象监视器上等待的所有线程。
            void wait()
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
            void wait(long timeout)
          在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。
        *
        * */

        Student student = new Student();
        SetThread setThread = new SetThread(student);
        GetThread getThread = new GetThread(student);
        setThread.start();
        getThread.start();
    }
}


2.内存可见性问题 volatile

volatile 解决内存可见性问题
     一、Java内存模型
     想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的。
     Java内存模型规定了所有的变量都存储在主内存中。每条线程中还有自己的工作内存,
     线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来)。
     线程对变量的所有操作(读取,赋值)都必须在工作内存中进行。
     不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。

     3.Java中的可见性
    
     对于可见性,Java提供了volatile关键字来保证可见性。
     当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
     而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,
     当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
     另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,
     并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。
     
volatile 关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。
                相较于 synchronized 是一种较为轻量级的同步策略。
               
volatile 变量,用来确保将变量的更新操作通知到其他线程。
可以将 volatile 看做一个轻量级的锁,但是又与
锁有些不同:
 对于多线程,不是一种互斥关系 
 不能保证变量状态的“原子性操作” 

public class MyTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread th = new Thread(myRunnable);
        th.start();
        while (true) {
            if (myRunnable.getFlag()) {
                System.out.println("进来了");
                break;
            }
        }
    }
}

class MyRunnable implements Runnable {
    //
  /*  对于可见性,Java提供了volatile关键字来保证可见性。
    当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

    volatile 关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。
 	           相较于 synchronized 是一种较为轻量级的同步策略。

 	           volatile 关键字:他不能解决原子性问题

    */

    volatile boolean flag = false;

    public boolean getFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        this.flag = true;
        System.out.println("线程执行了,flag:" + flag);
    }
}


3.CAS 算法

CAS 算法
 CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器
操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并
发访问。
 CAS 是一种无锁的非阻塞算法的实现。
 CAS 包含了 3 个操作数:
 需要读写的内存值 V 内存
 进行比较的值 A 预估值
 拟写入的新值 B 
 当且仅当 V 的值等于 A 时, CAS 通过原子方式用新值 B 来更新 V 的
   值,否则不会执行任何操作。
  jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronouse同步锁的一种乐观锁。JDK 5之前Java语言是靠synchronized关键字保证同步的,这是一种独占锁,也是是悲观锁。
  
 java.util.concurrent.atomic 包下提供了一些原子操作的常用类:
 AtomicBoolean 、 AtomicInteger 、 AtomicLong 、 AtomicReference
 AtomicIntegerArray 、 AtomicLongArray
 AtomicMarkableReference
 AtomicReferenceArray 

public class MyTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        for (int i = 0; i < 10; i++) {
            new Thread(myRunnable).start();
        }
    }
}

class MyRunnable implements Runnable {
    //static int num = 1;
    AtomicInteger i= new AtomicInteger(1);

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i.getAndIncrement());
        }

    }
}


4.线程的状态转换图及常见执行情况

 新建 , 就绪 , 运行 , 冻结 , 死亡
   新建:线程被创建出来 
   就绪:具有CPU的执行资格,但是不具有CPU的执行权
   运行:具有CPU的执行资格,也具有CPU的执行权
   阻塞:不具有CPU的执行资格,也不具有CPU的执行权
   死亡:不具有CPU的执行资格,也不具有CPU的执行权

Java中线程的状态分为6种:

  1. 初始(New):

新创建了一个线程对象,但还没有调用start()方法。

  1. 可运行(Runnable):

  1. 调用线程的start()方法,此线程进入就绪状态。就绪状态只是说你资格运行,调度程序没有给你CPU资源,你就永远是就绪状态。

  2. 当前线程sleep()方法结束,其他线程join()结束,等待用户输入完毕,某个线程拿到对象锁,这些线程也将进入就绪状态。

  3. 当前线程时间片用完了,调用当前线程的yield()方法,当前线程进入就绪状态。

  4. 锁池里的线程拿到对象锁后,进入就绪状态

  1. 运行中(Running)

就绪状态的线程在获得CPU时间片后变为运行中状态(running)。这也是线程进入运行状态的唯一的一种方式。

  1. 阻塞(Blocked):

阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。

  1. 等待(Waiting) 跟 超时等待(Timed_Waiting):

  1. 处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒(通知或中断),否则会处于无限期等待的状态。

  2. 处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。

  1. 终止(Terminated):

当线程正常运行结束或者被异常中断后就会被终止。线程一旦终止了,就不能复生。

PS:

  1. 调用 obj.wait 的线程需要先获取 objmonitorwait会释放 objmonitor 并进入等待态。所以 wait()/notify() 都要与 synchronized 联用。

  2. 其实线程从阻塞/等待状态 到 可运行状态都涉及到同步队列等待队列的,这点在 AQS 有讲。

阻塞与等待的区别

阻塞

当一个线程试图获取对象锁(非JUC库中的锁,即synchronized),而该锁被其他线程持有,则该线程进入阻塞状态。它的特点是使用简单,由JVM调度器来决定唤醒自己,而不需要由另一个线程来显式唤醒自己,不响应中断。

等待

当一个线程等待另一个线程通知调度器一个条件时,该线程进入等待状态。它的特点是需要等待另一个线程显式地唤醒自己,实现灵活,语义更丰富,可响应中断。例如调用:Object.wait()Thread.join()以及等待 LockCondition

虽然 synchronizedJUC 里的 Lock 都实现锁的功能,但线程进入的状态是不一样的。synchronized 会让线程进入阻塞态,而 JUC 里的 Lock是用park()/unpark() 来实现阻塞/唤醒 的,会让线程进入等待状态。虽然等锁时进入的状态不一样,但被唤醒后又都进入Runnable状态,从行为效果来看又是一样的。


5.线程池的概述和使用

1.为什么要使用线程池

在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,服务器在创建和销毁线程上花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,服务器应用程序需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务,这就是“池化资源”技术产生的原因。

线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。

2.线程池的组成部分 一个比较简单的线程池至少应包含线程池管理器、工作线程、任务列队、任务接口等部分。其中线程池管理器的作用是创建、销毁并管理线程池,将工作线程放入线程池中;工作线程是一个可以循环执行任务的线程,在没有任务是进行等待;任务列队的作用是提供一种缓冲机制,将没有处理的任务放在任务列队中;任务接口是每个任务必须实现的接口,主要用来规定任务的入口、任务执行完后的收尾工作、任务的执行状态等,工作线程通过该接口调度任务的执行。 线程池管理器至少有下列功能:创建线程池,销毁线程池,添加新任务。 工作线程是一个可以循环执行任务的线程,在没有任务时将等待。

任务接口是为所有任务提供统一的接口,以便工作线程处理。任务接口主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等。

3.线程池适合应用的场合 当一个服务器接受到大量短小线程的请求时,使用线程池技术是非常合适的,它可以大大减少线程的创建和销毁次数,提高服务器的工作效率。但是线程要求的运动时间比较长,即线程的运行时间比

1.线程池概述
    程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
    而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
    线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
    在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
2.内置线程池的使用概述
    JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
        public static ExecutorService newCachedThreadPool():            根据任务的数量来创建线程对应的线程个数    
        public static ExecutorService newFixedThreadPool(int nThreads):    固定初始化几个线程
        public static ExecutorService newSingleThreadExecutor():        初始化一个线程的线程池
    这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
        Future<?> submit(Runnable task)
        <T> Future<T> submit(Callable<T> task)
    使用步骤:
        创建线程池对象
        创建Runnable实例
        提交Runnable实例
        关闭线程池

根据任务的数量来创建线程对应的线程个数 :

public class MyTest {
    public static void main(String[] args) {
        //线程池:
        // JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法

        //ExecutorService 线程池
        // 根据任务的数量来创建线程对应的线程个数
        ExecutorService executorService = Executors.newCachedThreadPool();

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println(Thread.currentThread().getName() + "任务执行了");
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println(Thread.currentThread().getName() + "任务执行了");
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println(Thread.currentThread().getName() + "任务执行了");
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    System.out.println(Thread.currentThread().getName() + "任务执行了");
                }
            }
        });

        executorService.shutdown(); //关闭线程池

    }
}

 固定初始化几个线程:

public class MyTest2 {
    public static void main(String[] args) {
       // 固定初始化几个线程
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName()+"=="+i);
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
            }
        });
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + "==" + i);
                }
            }
        });


        executorService.shutdown();
    }
}

初始化一个线程的线程池:

public class MyTest3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //初始化一个线程的线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum=0;
                for (int i = 0; i <=100; i++) {
                    sum+=i;
                }
                return sum;
            }
        };

        Future<Integer> submit = executorService.submit(callable);
        Integer integer = submit.get();
        System.out.println(integer);
    }
}

public class MyTest4 {
    public static void main(String[] args) {
        //corePoolSize表示常驻核心线程数,如果大于0,即使本地任务执行完也不会被销毁
        //maximumPoolSize表示线程池能够容纳可同时执行的最大线程数
        //keepAliveTime表示线程池中线程空闲的时间,当空闲时间达到该值时,线程会被销毁,只剩下 `corePoolSize` 个线程位置
        //workQueue当请求的线程数大于 `maximumPoolSize` 时,线程进入该阻塞队列
        //threadFactory顾名思义,线程工厂,用来生产一组相同任务的线程,同时也可以通过它增加前缀名,虚拟机栈分析时更清晰
        //handler执行拒绝策略,当 workQueue 达到上限,同时也达到 maximumPoolSize 就要通过这个来处理,比如拒绝,丢弃等,这是一种限流的保护措施
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 100, 60L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(200000,
                true), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());

        threadPoolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName()+"==="+i);
                }
            }
        });

        threadPoolExecutor.shutdown();

    }
}


6.匿名内部类的方式实现多线程程序

匿名内部类的方式实现多线程程序
    
    new Thread(){代码…}.start();
    new Thread(new Runnable(){代码…}).start();

public class MyTest {
    public static void main(String[] args) {
        //匿名内部类的方式,来创建线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程执行了");
            }
        }).start();

        new Thread(){
            @Override
            public void run() {
                System.out.println("aaaaaaaaaaaaaaaaaaaaaaa");
            }
        }.start();
    }
}


7.定时器的概述和使用

1.定时器概述
    定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。
    在Java中,可以通过Timer和TimerTask类来实现定义调度的功能。
2.Timer和TimerTask
    Timer:
        public 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):
    TimerTask:定时任务
        public abstract void run()
        public boolean cancel()
    开发中
        Quartz是一个完全由java编写的开源调度框架。

public void schedule(TimerTask task, long delay):

public class MyTest2 {
    public static void main(String[] args) {
     // Timer 定时器  一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
        Timer timer = new Timer();
        //延迟执行定时任务 类 TimerTask 由 Timer 安排为一次执行或重复执行的任务。
        MyTimerTask myTimerTask = new MyTimerTask(timer);
        timer.schedule(myTimerTask,3000);
        //取消定时任务
        //myTimerTask.cancel();
    }
}

class MyTimerTask extends TimerTask{

    private Timer timer;

    public MyTimerTask(Timer timer) {
        this.timer = timer;
    }

    @Override
    public void run() {
        System.out.println("砰~~~~爆炸了!!!");
        //退出定时器
        timer.cancel();
    }
}

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

public class MyTest {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("叮铃铃~懒虫起床了~~~~");
            }
        },3000,1000); //第一次是3秒之后执行,以后每隔1秒重复执行。
    }
}

 public void schedule(TimerTask task,  Date time):

public class MyTest2 {
    public static void main(String[] args) throws ParseException {
        Timer timer = new Timer();
        Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2021-12-08 10:35:30");
        MyTimerTask myTimerTask = new MyTimerTask(timer);
        timer.schedule(myTimerTask,date);

    }
}

class MyTimerTask extends TimerTask {

    private Timer timer;

    public MyTimerTask(Timer timer) {
        this.timer = timer;
    }

    @Override
    public void run() {
        System.out.println("生日快乐!宝宝!");
        //退出定时器
        timer.cancel();
    }
}

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

public class MyTest2 {
    public static void main(String[] args) throws ParseException {
        Timer timer = new Timer();
        Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2021-12-08 10:40:00");
        MyTimerTask myTimerTask = new MyTimerTask(timer);
        timer.schedule(myTimerTask,date,1000);

    }
}

class MyTimerTask extends TimerTask {

    private Timer timer;

    public MyTimerTask(Timer timer) {
        this.timer = timer;
    }

    @Override
    public void run() {
        System.out.println("生日快乐!宝宝!");
        //退出定时器
        //timer.cancel();
    }
}


8.定时器的练习

定时任务的多次执行代码体现
    定时删除指定的带内容目录

public class demo5 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        MyTimerTask myTimerTask = new MyTimerTask(timer);
        timer.schedule(myTimerTask,3000);
    }
}

class MyTimerTask extends TimerTask{
    private Timer timer;

    public MyTimerTask(Timer timer) {
        this.timer = timer;
    }

    @Override
    public void run() {
        File file = new File("C:\\Users\\xiaomi\\Desktop\\demo");
        digui(file);
        System.out.println("删除完成");
        timer.cancel();
    }

    private void digui(File file) {
        File[] files = file.listFiles();
        for (File f : files) {
            if (f.isFile()) {
                f.delete();
            }else {
                digui(f);
            }
        }
        file.delete();
    }
}


9.设计模式的概述和分类

1.设计模式概述
    设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编写、代码设计经验的总结。
    使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性以及代码的结构更加清晰.
2.设计模式分类
    创建型模式(创建对象的):    单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
    行为型模式(对象的功能):    适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
    结构型模式(对象的组成):    模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。


10.简单工厂模式概述和使用

1.简单工厂模式概述:    又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
2.优点:    使用静态工厂模式的优点是实现责任的分割,该模式的核心是工厂类,工厂类含有必要的选择逻辑,可以决定什么时候创建哪一个产品的实例,
        而客户端则免去直接创建产品的责任,而仅仅是消费产品。也就是说静态工厂模式在不改变客户端代码的情况可以动态的增加产品。
        明确了类的职责
3.缺点
    这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,
    就需要不断的修改工厂类,不利于后期的维护
4.案例演示

Animal类:

abstract class Animal {
    public abstract void eat();
}

Dog类:

public class Dog extends Animal{
    public void eat(){
        System.out.println("狗吃骨头");
    }
}

Cat类:

public class Cat extends Animal{
    public void eat(){
        System.out.println("猫吃鱼");
    }
}

Tiger类:

public class Tiger extends Animal{
    @Override
    public void eat() {
        System.out.println("老虎吃肉");
    }
}

AnimalFactory类:

//工厂类,负责生产动物
public class AnimalFactory {
    /*
    public static Cat getCat(){
        return new Cat();
    }

    public static Dog getDog() {
        return new Dog();
    }*/

    public static Animal getAnimal(String name) {
        if("cat".equals(name)){
            return new Cat();
        }else if("dog".equals(name)){
            return new Dog();
        } else if ("tiger".equals(name)) {
            return new Tiger();
        }else{
            return null;
        }
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) {
        Animal an = AnimalFactory.getAnimal("cat");
        an.eat();
        an=AnimalFactory.getAnimal("dog");
        an.eat();
        an=AnimalFactory.getAnimal("tiger");
        an.eat();
    }
}


11.工厂方法模式的概述和使用

1.工厂方法模式概述
    工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现。
2.优点
    客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,
    只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性
3.缺点:    需要额外的编写代码,增加了工作量
案例演示

MaxAnimalFactory类:

//大的动物工厂,认为是总公司
public interface MaxAnimalFactor {
    public Animal getAnimal();
}

Animal类:

abstract class Animal {
    public abstract void eat();
}

Cat类:

public class Cat extends Animal{
    public void eat(){
        System.out.println("猫吃鱼");
    }
}

Dog类:

public class Dog extends Animal{
    public void eat(){
        System.out.println("狗吃骨头");
    }
}

Tiger类:

public class Tiger extends Animal{
    @Override
    public void eat() {
        System.out.println("老虎吃肉");
    }
}

CatFactory类:

//猫工厂
public class CatFactory implements MaxAnimalFactor{
    @Override
    public Animal getAnimal() {
        return new Cat();
    }
}

DogFactory类:

public class DogFactory implements MaxAnimalFactor{
    @Override
    public Animal getAnimal() {
        return new Dog();
    }
}

TigerFactory类:

public class TigerFactory implements MaxAnimalFactor {
    @Override
    public Animal getAnimal() {
        return new Tiger();
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) {
        CatFactory catFactory = new CatFactory();
        Animal animal = catFactory.getAnimal();
        animal.eat();
        Animal animal1 = new DogFactory().getAnimal();
        animal1.eat();
        Animal animal2 = new TigerFactory().getAnimal();
        animal2.eat();
    }
}


12.单例模式之懒汉式

1.案例演示:    单例模式之懒汉式
2.饿汉式和懒汉式的区别
    
        单例设计模式之懒汉式

         开发中        饿汉式
         面试中        懒汉式

         面试就是想面试你们的两种思想:

             a: 线程安全思想
             b: 延迟加载思想

懒汉式,你需要才帮你new:

Student类:

//单例模式值懒汉式
public class Student {
    private static Student student=null;
    //1.私有构造
    private Student() {}
    //2.对外提供一个公共的方法,返回该类的实例  synchronized 保证在多线程环境下也是单例的
    public synchronized static  Student getStudent(){
        if (student == null) {
            student=new Student(); //延迟加载 你需要的时候,才会帮你创建一个
        }
        return student;
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) throws InterruptedException {
        //单例:在内存中只存在该类的一个实例
        //单例模式:保证该类的实例在内存中只有一份。
        Student student = Student.getStudent();
        Student student1 = Student.getStudent();
        System.out.println(student);
        System.out.println(student1);
        System.out.println(student==student1);

    }
}

饿汉式,类一加载就会创建出来:

Teacher类:

//饿汉式,类一加载,就创建一个Teacher读写
public class Teacher {
    private static final Teacher teacher=new Teacher();
    //私有构造
    private Teacher() {}
    public static Teacher getTeacher(){
        return teacher;
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) {
        Teacher teacher = Teacher.getTeacher();
        Teacher teacher1 = Teacher.getTeacher();
        System.out.println(teacher);
        System.out.println(teacher1);
        System.out.println(teacher==teacher1);
    }
}


13.单例模式的Java代码体现Runtime类

1.Runtime类概述
    每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。 
    应用程序不能创建自己的 Runtime 类实例。 
2.案例演示:    public Process  exec(String command)  //执行Dos 命令
3.查看Runtime的源码:        发现是单例模式的应用

可以执行Dos命令:

public class MyTest {
    public static void main(String[] args) throws IOException {
        Runtime runtime = Runtime.getRuntime();
        //可以执行DOS命令
        runtime.exec("calc");
        runtime.exec("mspaint");
    }
}

关机演示:

public class MyTest2 {
    public static void main(String[] args) throws ParseException {
        Timer timer = new Timer();
        String dateStr="2021-12-10 11:07:00";
        Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(dateStr);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    Runtime.getRuntime().exec("shutdown /s /t 0");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        },date);
    }
}


14.模版设计模式概述和使用

1.需求: 计算一个for循环执行的时间
2.模版设计模式概述
    模版方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现
3.优点和缺点
    a:优点:    使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求
    b:缺点:    如果算法骨架有修改的话,则需要修改抽象类
4.案例演示:    模版设计模式的使用

CalcTime类(把这个类中testTime方法定义为抽象的,然后定义一个子类,重写这个方法):

public abstract class CalcTime {
    public long getTime() throws IOException {
        long start = System.currentTimeMillis();
        testTime();
        long end = System.currentTimeMillis();
        return end-start;
    }

    public abstract void testTime() throws IOException;

   /* private void testForTime() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(i);
        }
    }

    private void testCopyTime() throws IOException {
         Files.copy(Paths.get("辛晓琪 - 领悟.mp3"),Paths.get("辛晓琪 - 领悟2.mp3"), StandardCopyOption.REPLACE_EXISTING);
    }
*/

}

TestForTime:

public class TestForTime  extends CalcTime {
    @Override
    public void testTime() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(i);
        }
    }
}

TestCopyTime:

public class TestCopyTime extends CalcTime {
    @Override
    public void testTime() throws IOException {
        Files.copy(Paths.get("辛晓琪 - 领悟.mp3"), Paths.get("辛晓琪 - 领悟2.mp3"), StandardCopyOption.REPLACE_EXISTING);
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) throws IOException {
         CalcTime calcTime = new TestForTime();
         calcTime=new TestCopyTime();
         long time = calcTime.getTime();
         System.out.println("耗时:"+time);
    }
}


15.装饰模式概述和使用

1.装饰模式概述:    装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例交给装饰类。是继承的替代方案
2.优点和缺点
    a:优点
        使用装饰模式,可以提供比继承更灵活的扩展对象的功能,它可以动态的添加对象的功能,
        并且可以随意的组合这些功能。
    b:缺点:    正因为可以随意组合,所以就可能出现一些不合理的逻辑。
3.案例演示:    装饰模式的使用

Phone类:

public abstract class Phone{
    public abstract void call();
}

IPhone类:

public class IPhone extends Phone {
    @Override
    public void call() {
        System.out.println("最基本的打电话功能");
    }
}

DecorationPhone(装饰类):

public class DecorationPhone extends Phone{
    private Phone phone;
    //把你要装饰的那个 phone传进来
    public DecorationPhone(Phone phone) {
        this.phone = phone;
    }
    @Override
    public void call() {
        phone.call();
    }
}

BeautyPhone类:

public class BeautyPhone extends DecorationPhone {
    public BeautyPhone(Phone phone) {
        super(phone);
    }
    @Override
    public void call() {
        super.call();
        System.out.println("视频美颜功能");
    }
}

VideoPhone类:

public class VideoPhone extends DecorationPhone {
    public VideoPhone(Phone phone) {
        super(phone);
    }

    @Override
    public void call() {
        super.call();
        System.out.println("视频通话功能");
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) throws FileNotFoundException {
        Phone iPhone = new IPhone();
        //iPhone.call();
        //增强 call这个功能,定义一个装饰类,来增强,你想要增强的功能
        //VideoPhone videoPhone = new VideoPhone(iPhone);
        // videoPhone.call();
        //BeautyPhone beautyPhone = new BeautyPhone(iPhone);
        // beautyPhone.call();

        // VideoPhone videoPhone = new VideoPhone(new BeautyPhone(iPhone));
        //  videoPhone.call();

        BeautyPhone beautyPhone = new BeautyPhone(new VideoPhone(iPhone));
        beautyPhone.call();
        //IO
        BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("a.txt")));
    }
}

Cake类:

public abstract class Cake {
    public abstract String getCakeMsg();
    public abstract BigDecimal getPrice();
}

BaseCake:

public class BaseCake extends Cake {
    @Override
    public String getCakeMsg() {
        return "我是一个8英寸的普通蛋糕";
    }

    @Override
    public BigDecimal getPrice() {
        return new BigDecimal("68");
    }
}

CakeDecorator:

public abstract class CakeDecorator extends Cake{
    private Cake cake;

    //把你要装饰的蛋糕传进来
    public CakeDecorator(Cake cake) {
        this.cake = cake;
    }

    @Override
    public String getCakeMsg() {
        return this.cake.getCakeMsg();
    }

    @Override
    public BigDecimal getPrice() {
        return this.cake.getPrice();
    }
}

CakeAddGrapeDecorator:

public class CakeAddGrapeDecorator extends CakeDecorator {
    public CakeAddGrapeDecorator(Cake cake) {
        super(cake);
    }

    @Override
    public String getCakeMsg() {
        return super.getCakeMsg() + "+1个葡萄";
    }

    @Override
    public BigDecimal getPrice() {
        return super.getPrice().add(new BigDecimal("5"));
    }
}

CakeAddMangoDecorator:

public class CakeAddMangoDecorator extends CakeDecorator {

    public CakeAddMangoDecorator(Cake cake) {
        super(cake);
    }

    @Override
    public String getCakeMsg() {
        return super.getCakeMsg() + "+1个芒果";
    }

    @Override
    public BigDecimal getPrice() {
        return super.getPrice().add(new BigDecimal("10"));
    }
}

TestCakeDecorator:

public class TestCakeDecorator {
    public static void main(String[] args) {
        Cake cake = null;
        //普通蛋糕
        cake = new BaseCake();
        System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice());
        //加一个芒果
        cake = new CakeAddMangoDecorator(cake);
        System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice());
        //加一个葡萄
        cake = new CakeAddGrapeDecorator(cake);
        System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice());
        //再加一个芒果
        cake = new CakeAddMangoDecorator(cake);
        System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice());


    }
}


16.观察者模式概述和使用

1.案例: 找猎头找工作
2.观察者 = 订阅者 + 发布者
     岗位类  求职者  猎头(注册方法,注销方法,发布方法)

Headhunters:

//猎头
public class Headhunters {
    //定义两个集合,一个用来放求职者,一个用来放工作岗位
    private ArrayList<Worker> workers=new ArrayList<>();
    private ArrayList<Job> jobs=new ArrayList<>();

    //注册求职者
    public void addWoker(Worker worker){
        workers.add(worker);
    }

    //注销求职者
    public void removeWoker(Worker worker) {
        workers.remove(worker);
    }

    //更新岗位信息

    public void updateJob(Job job) {
        //工作岗位更新了,通知观察者(求职者)
       jobs.add(job);
       //通知
        notifyAllWoker(workers,job);
    }

    //通知求职者
    private void notifyAllWoker(ArrayList<Worker> workers, Job job) {
        for (Worker worker : workers) {
            System.out.println(worker.getName()+"你好!有一份工作:"+job.getJobName()+"薪资:"+job.getSal()+"欢迎你前去面试!");
        }
    }

}

Job:

//岗位
public class Job {
    private String JobName;
    private double sal;

    public Job() {
    }

    public Job(String jobName, double sal) {
        JobName = jobName;
        this.sal = sal;
    }

    public String getJobName() {
        return JobName;
    }

    public void setJobName(String jobName) {
        JobName = jobName;
    }

    public double getSal() {
        return sal;
    }

    public void setSal(double sal) {
        this.sal = sal;
    }
}

Worker:

public class Worker {
    private String name;

    public Worker() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) {
        Worker w1 = new Worker("张三");
        Worker w2 = new Worker("李四");
        Worker w3 = new Worker("王五");
        Worker w4 = new Worker("赵六");

        Headhunters headhunters = new Headhunters();
        headhunters.addWoker(w1);
        headhunters.addWoker(w2);
        headhunters.addWoker(w3);
        headhunters.addWoker(w4);
        Job job = new Job("Java开发工程师", 600000);
        headhunters.updateJob(job);
        System.out.println("=======================");

        Job job2 = new Job("前端开发工程师", 800000);
        headhunters.updateJob(job2);

        headhunters.removeWoker(w4);
        System.out.println("=====================");

        Job job3 = new Job("运维工程师", 8888);
        headhunters.updateJob(job3);
    }
}


17.建造者模式

经常碰见的 XxxBuilder 的类,通常都是建造者模式的产物。建造者模式其实有很多的变种,但是对于客户端来说,我们的使用通常都是一个模式的:
Food food = new FoodBuilder (). a (). b (). c (). build ();
Food food = Food . builder (). a (). b (). c (). build ();
套路就是先 new 一个 Builder,然后可以链式地调用一堆方法,最后再调用一次 build() 方法,我们需要的对象就有了。
User类:
class User {
    //下面是“一堆”的属性
    private String name;
    private String password;
    private String nickName;
    private int age;

    public String getName() {
        return name;
    }

    public String getPassword() {
        return password;
    }

    public String getNickName() {
        return nickName;
    }

    public int getAge() {
        return age;
    }

    // 构造方法私有化,不然客户端就会直接调用构造方法了
    private User(String name, String password, String nickName, int age) {
        this.name = name;
        this.password = password;
        this.nickName = nickName;
        this.age = age;
    }

    /*  静态方法,
      用于生成一个 Builder,这个不一定要有,不过写这个方法是一个很好的习惯,
      有些代码要求别人写 new User.UserBuilder().a()   build()看上去就没那么好*/
    public static UserBuilder builder() {
        return new UserBuilder();//返回内部类的对象
    }

    //内部类
    public static class UserBuilder {
        //下面是和 User一模一样的一堆属性
        private String name;
        private String password;
        private String nickName;
        private int age;

        private UserBuilder() {
        }

        //链式调用设置各个属性值,返回 this,即 UserBuilder
        public UserBuilder name(String name) {
            this.name = name;
            return this;
        }

        public UserBuilder password(String password) {
            this.password = password;
            return this;
        }

        public UserBuilder nickName(String nickName) {
            this.nickName = nickName;
            return this;
        }

        public UserBuilder age(int age) {
            this.age = age;
            return this;
        }

     /*   build() 方法负责将 UserBuilder 中设置好的属性“复制” 到 User
        中。当然,可以在 “复制”之前做点检验*/
        public User build() {
            return new User(name, password, nickName, age);
        }
    }
}

测试类:

public class MyTest {
    public static void main(String[] args) {
        User user = User.builder().name("张三").age(23).nickName("老三").password("123456").build();
        String name = user.getName();
        System.out.println(name);
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值