0
点赞
收藏
分享

微信扫一扫

Java第22天

知年_7740 2022-02-19 阅读 100

一、多线程的常用的方法

在这里插入图片描述
stop() 停止线程 方法已过时了因为不安全 但还是有效果 一般不使用,建议用wait()
interrupt();只能短暂停止,还是会运行完线程

package day22;

public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
           /* if(i==5){
                //停止线程   stop()已过时了 用interrupt()
                //stop();
               interrupt();//只能短暂停止,还是会运行完线程
            }*/
            System.out.println(Thread.currentThread().getName()+"\t"+i);
        }
    }
}

package day22;

public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        MyThread th1 = new MyThread();
        th1.start();
        Thread.sleep(2000);
        //结束当前线程
        //th1.stop();
       th1.interrupt();
    }
}

yield() 是静态方法 直接通过类名调用 不建议用对象名调用 暂停当前正在运行的线程 执行其他线程
阿里规范 用对象名调用静态方法增加编译器解析成本,直接用类名来访问即可。

package day22;

public class Test02 {
    public static void main(String[] args) {
        MyThread th1 = new MyThread();
        th1.start();
        //yield() 是静态方法 直接通过类名调用 不建议用对象名调用
        //阿里规范 用对象名调用增加编译器解析成本,直接用类名来访问即可。
        //暂停当前正在运行的线程 执行其他线程
        Thread.yield();
        MyThread th2 = new MyThread();
        th2.start();
    }
}

join()表示等待该线程执行完毕之后 其它线程才能执行(方法调用必须是在开启线程之后调用才有效果)

package day22;

public class Test03 {
    public static void main(String[] args) throws InterruptedException {
        MyThread th1 = new MyThread();
        th1.start();
        //表示等待改线程执行完毕之后 其它线程才能执行(方法调用必须是在开启线
        //程之后调用才有效果)
        th1.join();
        MyThread th2 = new MyThread();
        th2.start();
    }
}

二、Object 提供线程中的使用的方法

在这里插入图片描述
需求分析
在这里插入图片描述
其原理就是根据包子存不存在就行等待休眠,同时锁住所有操作,因为包子一种状态对应不同线程的不同操作,如果不锁住,就会抢发生IllegalMonitorStateException 非法监视器状态异常
这个异常就是 线程1 刚生产了一个包子,线程2抢到消费了,包子的存在对线程1就是问题,计算机理解不了,回忆之前未使用锁时,两个线程循环打印1~100,并不是等线程1打印完线程2在打印,而是谁抢到了打印谁的,包子的状态时共有的,线程抢占就会有冲突。
或者就是单纯不能直接使用wait();notify()
在线程中调用wait方法的时候 要用synchronized锁住对象,确保代码段不会被多个线程调用,即需要obj.wait();obj.notify();obj为锁对象

包子存在就 消费者去吃,吃完唤醒老板,老板就等待
包子不存在就消费者等待,老板生产,做完包子唤醒消费者

package day22;

public class Test04 {
    public static void main(String[] args) {
        //定义一个锁的对象
        Object obj = new Object();
        //买包子消费者线程
        new Thread(){
            @Override
            public void run() {
                synchronized (obj){
                    //第一步
                    System.out.println("我需要一个包子");
                    //需要无线进行等待
                    try {
                        //第二步
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("==========");
                    //第五步
                    System.out.println("我正在吃包子");
                }
            }
        }.start();
        //老板线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(obj){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    //第三步
                    System.out.println("我的包子已经做好了,可以开始吃了");
                    //需要唤醒 消费者 可以来吃包子
                    //第四步
                    obj.notify();
                }
            }
        }).start();
    }
}

三、多线程中的生产者与消费者模式

需求分析
在这里插入图片描述
包子类

package day22;

public class BaoZi {
    public String pi;
    public String xian;
    //定义一个标记表示是否存在包子 false 没有包子 ture 有包子
    boolean flag = false;
    //boolean 默认值为false
}

生产者

package day22;

public class ProducterThread extends  Thread{
    private BaoZi baoZi;
    public  ProducterThread(BaoZi baoZi){
        this.baoZi = baoZi;
    }

    @Override
    public void run() {
        //一直生产包子
        while (true){
            //同步代码块 //互斥锁
            synchronized(baoZi){
                //已存在包子 等待消费  不需要在生产
                if(baoZi.flag){
                    try {
                        baoZi.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //不存在包子 开始生产包子
                baoZi.pi = "薄皮";
                baoZi.xian ="韭菜鸡蛋";
                System.out.println("==============");
                System.out.println("我生产了一个"+baoZi.pi+baoZi.xian+"的包子");
                //将标记值设置为ture
                baoZi.flag = true;
                //通知消费者去消费
                baoZi.notify();
            }
        }
    }
}

消费者

package day22;

public class CustomerThread extends Thread{
    private  BaoZi baoZi;
    public CustomerThread(BaoZi baoZi){
        this.baoZi=baoZi;
    }

    @Override
    public void run() {
        while (true){
            synchronized (baoZi){
                //如果包子不存在 等包子生成出来
                if(!baoZi.flag){
                    //无线等待
                    try {
                        baoZi.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //包子存在 吃包子
                System.out.println("我消费了一个"+baoZi.pi+baoZi.xian+"的包子");
                baoZi.pi=null;
                baoZi.xian=null;
                //标记设置为false
                baoZi.flag = false;
                //通知生产者开始生产
                baoZi.notify();
            }
        }
    }
}

测试类
多个类需要一个共有的变量,即将这个变量封装为一个类的属性,在测试类中new 这个类实例化为对象,然后将此对象作为参数传入需要的多个类中,就可以实现共享对象的属性即变量,且需要的类定义此对象为私有变量,并提供有参构造

package day22;

public class Test05 {
    public static void main(String[] args) {
        BaoZi baoZi =new BaoZi();
        //开启生产者的线程
        new ProducterThread(baoZi).start();
        //开启消费者的线程
        new CustomerThread(baoZi).start();
    }
}

四、多线程的第四种实现方式(实现类连接Callable接口-带返回值)

步骤
定义一个类 实现Callable 这个接口
实现接口中的抽象方法 cell()
实例化 实现Callable的类
实例化任务的对象 FutureTask 构造方法的参数 需要传递 Callable 的实现类
实例化Thead对象 构造方法中需要传递 任务对象 调用start() 开启线程
根据任务对象的get() ==>获取返回值

案例 实现多线程1-100之和 获取其返回值结果

package day22;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        Integer count =0;
        for (int i = 0; i < 100; i++) {
            count+=i;
        }
        System.out.println(Thread.currentThread().getName());
        return count;
    }
}

package day22;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class Test06 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //实例化MyCallable对象
        MyCallable call = new MyCallable();
        //实例化任务对象
        FutureTask<Integer> ft = new FutureTask<>(call);
        //实例化Thread对象
        new Thread(ft).start();
        //调用任务对象的get方法 获取其返回值
        Integer count = ft.get();
        System.out.println(count);
    }


}

五、枚举

1.枚举的概念:用于间接的表示一些固定的值 Java提供了枚举来进行表示
在枚举中可以将变量 一一进行罗列 而枚举的值必须是罗列的值 限制的数据的范围
2.定义的格式
访问修饰符 enum 枚举的名称 {
枚举项
}
enum 是枚举的关键字
3.代码
public enum Color {
//枚举的选项
BLUE,YELLOW,RED;
}
4.枚举的特点
1.所有的枚举类都是Enum子类
2.每一个枚举项都一个对象
3.每一个枚举项都可以通过枚举类来进行获取 枚举类.枚举项
4.一般枚举项是编写在第一行 不同的枚举项以,来进行分割 最后以分号进行结尾
5.枚举类中可以有成员变量
4.枚举类中可以有构造 但是只能是私有的构造 默认的也是私有的构造
5.枚举类中可以有抽象方法,但是每一个枚举项都必须实现其抽象方法

5.代码
//枚举的选项 BLUE,YELLOW,RED都可以理解是Color的子类对象

package day22;

public enum  Color {
    //枚举的选项 BLUE,YELLOW,RED都可以理解是Color的子类对象
    BLUE{
        @Override
        public String getColor() {
            return "蓝色";
        }
    },YELLOW{
        @Override
        public String getColor() {
            return "黄色";
        }
    },RED{
        @Override
        public String getColor() {
            return "红色";
        }
    };
    private String name;
    private Color(){

    }
    public abstract String getColor();

}


6.枚举中常用的一些方法
在这里插入图片描述

package day22;

public class Test07 {
    public static void main(String[] args) {
        /*//输出结果为BLUE YELLOW RED
        System.out.println(Color.BLUE);
        System.out.println(Color.YELLOW);
        System.out.println(Color.RED);*/
        Color c1 = Color.RED;
        Color c = Color.YELLOW;
        System.out.println(c.name());
        System.out.println(c.ordinal());
        //c1-c的索引值之差
        System.out.println(c1.compareTo(c));
        System.out.println(c1.toString());
        System.out.println(Color.valueOf("YELLOW"));
        Color[] values = Color.values();
        for (Color value : values) {
            System.out.println(value.name());
        }
    }
}

7.switch与枚举进行搭配的使用

package day22;

public enum Week {
    星期一,星期二,星期三,星期四,星期五,星期六,星期天;
}

package day22;

public class Test08 {
    public static void main(String[] args) {
        Week w = Week.星期四;
        switch (w){
            case 星期一:
                System.out.println("猪脚饭");
                break;
            case 星期二:
                System.out.println("烧鸭饭");
                break;
            case 星期三:
                System.out.println("白切鸡");
                break;
            case 星期四:
                System.out.println("烧腊");
                break;
            case 星期五:
                System.out.println("猪排");
                break;

            case 星期六:
                System.out.println("茄子");
                break;
            case 星期天:
                System.out.println("土豆丝");
                break;
            default:
                break;
        }
    }
}

六、线程的生命周期

1.线程的基本状态
在这里插入图片描述
1、新建状态(New):新创建了一个线程对象。
2、就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
3、运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
4、阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
1)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
2)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
3)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
5、死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
原文地址
记忆:一个三角形 加外延两点 即 可运行 正在运行 阻塞 新建和死亡
具体的状态转换看下图

2.线程的生命周期
在这里插入图片描述
3.线程的状态
Thread.State ==> 通过枚举来进行一一罗列
4.获取线程状态的方法
public Thread.State getState()
5.代码
线程类

public class MyThread extends Thread {
@Override
public void run() {
}
}

测试类

public class Test {
public static void main(String[] args) {
MyThread th1 = new MyThread();
System.out.println(th1.getState());
th1.start();
System.out.println(th1.getState());
}
}

七、线程池

1.线程池:用于存储大量线程的的容器 就称为叫做线程池
2.没有线程池之前
在这里插入图片描述
3.有线程池之后
在这里插入图片描述
4.使用线程池的步骤
1.通过这个Executors工具类来获取获取线程池对象 并且设置线程的个数
newFixedThreadPool(设置线程池的个数)
newSingleThreadExecutor() 线程池的线程的个数1
2.创建任务的对象
定义一个类 实现Runnable
定义一个类 实现Callable
3.调用submit方法 提交任务到线程池中 submit(任务对象)
4.调用这个类Future的get() 可以获取到返回值
Executors 线程池工具的方法
在这里插入图片描述
创建任务的对象 Runnable 没有返回值 Callable 有返回值
1.实现Runnable接口

package day22.threadpool;

public class MyRunnable implements Runnable{

    @Override
    public void run() {
        System.out.println("哈哈哈");
    }
}

2.实现Callable

package day22.threadpool;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        Integer count =0;
        for (int i = 0; i < 100; i++) {
            count+=i;
        }
        return count;
    }
}

创建线程池,提交任务到线程池中

package day22.threadpool;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Test09 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //获取单个线程的线程池对象
        //创建一个线程
        ExecutorService service1 = Executors.newSingleThreadExecutor();
        //创建3个线程
        ExecutorService service2 = Executors.newFixedThreadPool(3);
        //不带返回值
        service2.submit(new MyRunnable());
        service2.submit(new MyRunnable());
        service2.submit(new MyRunnable());
        //带返回值
        Future f = service1.submit(new MyCallable());
        Integer count = (Integer) f.get();
        System.out.println(count);
    }
}

线程两个方法
在这里插入图片描述

package day22.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Test10 {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        executorService.submit(new MyRunnable());
        executorService.submit(new MyRunnable());
        executorService.submit(new MyRunnable());
        //执行已提交的任务,不会在接受新任务
        executorService.shutdown();
        //试图停止所有正在执行的活动任务,暂停处理正在等待的任务
        //完成时间短的任务一般先完成了,但是正在等待的任务一定会被停止,不执行
        executorService.shutdownNow();
    }
}

八、java中定时任务

1.生活中的定时任务就是闹钟 在开发中定时发送邮件 定时向文件中写入内容
2.定时任务使用的步骤
1.定义一个类 继承 TimerTask == > 表示是一个任务的对象
2.创建定时器对象 Timer
3.需要将任务提交到定时对象中
3.需求: 指定的时间向文件中写入内容 ==> 使用定时任务
4.代码

package day22.threadpool;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.TimerTask;

public class RemindTask extends TimerTask {
    @Override
    public void run() {
        System.out.println("我是定时任务");
        try {
            BufferedWriter bw = new BufferedWriter(new FileWriter("1.txt"));
            bw.write("定时任务");
            bw.flush();
            bw.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

如果d 在当前时间之前就会直接执行定时任务
如果d 在当前时间之后 就会等到指定d的时间才会执行定时任务

package day22.threadpool;

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;

public class Test11 {
    public static void main(String[] args) {

        long time = System.currentTimeMillis();
        Date d = new Date(time+5000);
        // 如果d 在当前时间之前就会直接执行定时任务
        //如果d 在当前时间之后 就会等到指定d的时间才会执行定时任务
        //Date d= new Date(time-5000);
        System.out.println(d);
        Timer t =new Timer();
        RemindTask ta = new RemindTask();
        t.schedule(ta,d);
    }
}

九、单例设置模式

1.特点: 有且仅实例化一个对象 保证对象的唯一
2.使用场景: 开发中的工具类 properties文件的加载 也只需要加载一次
3.有且仅实例化
1.私有的属性
2.私有的构造 ==> 只能在本类实例化这个对象
3.提供一个公有的方法
4.单例设置模式的分类 使用双重锁来实现单例设置模式
1.恶汉模式
优点: 没有锁机制 效率比较高
缺点: 项目初始化的时候 就进行加载 可能出现项目卡顿
代码

package day22.danli;

import java.text.SimpleDateFormat;
import java.util.Date;

/*时间的工具类
* 实例化一次*/
public class DateUtils {
    //私有的属性
    private static DateUtils utils = new DateUtils();
    //私有的构造
    private DateUtils(){

    }
    //提供一个公有的方法
    public static DateUtils getInstance(){
        if(utils!=null){
            return utils;
        }
        return null;
    }
    //将data对象转为字符串
    public String format(Date d){
        return new SimpleDateFormat("yyyy-MM-dd").format(d);
    }


}

2.懒汉模式
优点: 使用的时候进行加载 不是项目初始化就立即加载 懒加载
缺点: 在多线程不安全的
代码

package day22.danli;

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateUtils1 {
    private static DateUtils1 utils1;
    private DateUtils1(){

    }

    public static DateUtils1 getInstance(){
        if(utils1 == null){
            utils1 = new DateUtils1();
        }
        return utils1;
    }
    public String format(Date d){
        return new SimpleDateFormat("yyyy-MM-dd").format(d);
    }
}


3.双重锁(使用最多)
优点:使用多线程的锁的机制来保证多线程中的安全
缺点: 效率会降低
代码

package day22.danli;

public class DateUtils2 {
    //私有的属性 volatile 可见性 变量发生改变之后 所有线程获取到改变后的值
    private static volatile DateUtils2 utils2;
    //私有的构造
    private DateUtils2(){

    }
    //提供一个公有的方法
    public static synchronized DateUtils2 getInstance(){
        /*if(utils2 ==null){
            utils2=new DateUtils2();
        }*/
        if(utils2 == null){
            synchronized (DateUtils2.class){
               if (utils2 ==null){
                  utils2 = new DateUtils2();
               }
            }
        }
        return utils2;


    }



}


4.使用静态内部类

package day22.danli;

public class DateUtils3 {
    //静态的内部类
    private static class Holer{
        //在内部类中实例化外部类的对象
        public static final DateUtils3 utiils3 = new DateUtils3();
    }
    //私有的构造方法
    private DateUtils3(){

    }
    //提供一个公有的方法来进行访问
    public static DateUtils3 getInstance(){
        return Holer.utiils3;
    }

}

测试类

package day22.danli;

import java.util.Date;
import java.util.Scanner;

public class Test12 {
    public static void main(String[] args) {
        System.out.println(DateUtils.getInstance().format(new Date()));
        System.out.println(DateUtils.getInstance());
        System.out.println(DateUtils1.getInstance().format(new Date()));
        System.out.println(DateUtils1.getInstance());
        System.out.println(DateUtils2.getInstance());
        System.out.println(DateUtils3.getInstance());
        Scanner sc= new Scanner(System.in);


    }
}

举报

相关推荐

0 条评论