TwentyFourDay-java wait与notify,获取字节码

1、线程分为两大类

用户线程与守护线程(后台线程)

守护线程:

代表性的守护线程:垃圾回收机制。

特点:一般是个死循环,所有的用户线程只要结束。守护线程自动结束。

t1.setDaemon(true);//把分支线程设置为守护线程,用户线程结束,守护线程也结束

2、定时器

间隔特定的时间,执行特定的程序

利用sleep方法,是最原始的定时器

Java已经写好了一个定时器:java.util.Timer。

在实际开发中,使用较多的是Spring框架中提供的SpringTask框架。

//TimesThread线程

//TimerTask实现了Runnable接口,也是一个线程机制
class TimesThread extends TimerTask{
​
    @Override
    public void run() {
        Date date=new Date();
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        String d=sdf.format(date);
        System.out.println(d+"完成了一次备份!");
    }
}

//定时器

public static void main(String[] args) throws Exception{
    //创建一个定时器对象
    Timer time=new Timer();
    SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    Date date=sdf.parse("2021-07-17 09:50:30");
    //time.schedule(线程对象,程序起始时间,每个多长时间执行一次)
    time.schedule(new TimesThread(),date,1000*10);
}

3、线程的实现方式第三种

实现线程的第三种方式:实现Callable接口 优点:可以获取线程的执行结果 缺点:效率较低

public static void main(String[] args) {
        //创建一个“未来任务类”对象,参数是Callable接口实现类对象(可以收到返回值)
        //参数是Runnable接口实现类对象(接收不到返回值)
        FutureTask futureTask=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                System.out.println("begin");
                Thread.sleep(1000*5);//当前线程睡眠五秒
                System.out.println("end");
                int a=10;
                int b=10;
                return a+b;
            }
        });
        //把未来任务类对象封装为线程
        Thread thread=new Thread(futureTask);
        thread.start();
​
        //这行代码会阻塞主线程的执行,只有当支线程执行完毕,获取到值后才可以继续执行主线程
        try {
            //取返回值
            Object o = futureTask.get();
            System.out.println(o);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        //当get获取到分支线程的结果后才可以执行这段代码,时间可能很长
        System.out.println("主线程执行完毕");
    }

4、关于Object类中的wait与notify方法 (生产者和消费者模式)

第一:wait和notify方法不是线程对象的方法,是java中任何一个java对象的方法

wait方法和notify方法不是通过线程对象调用的。

第二:wait方法的作用?

Object o=new Object();

o.wait(); 表示在O上的活动线程进入无期限等待,直到被唤醒为止

第三:notify方法的作用?

o.notify();唤醒在o上等待的线程

notifyAll():唤醒所有在o上等待的线程

第四:wait与notify方法建立在线程同步的基础之上

小练习:

两个线程交替输出奇数和偶数如:t1-->1 t2-->2 t3-->3 t4-->4......

//创建一个数字类,用来累加

class Number {
    private int i;
    public Number(int i){
        this.i=i;
    }
​
    public int getI() {
        return i;
    }
​
    public void setI(int i) {
        this.i = i;
    }
​
    public void insertnum(int i){
        ++i;
        this.setI(i);
    }
}

//线程1

class OneThread extends Thread{
    private Number num;
    public OneThread(Number num){
        this.num=num;
    }
    public void run(){
        //把共享对象num锁住
        synchronized (num) {
            while (true) {
                //判断是不是偶数,要是偶数线程进入无限期等待
                if (num.getI() % 2 == 0) {
                    try {
                        num.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //程序走到这里说明,i是奇数,先睡眠两秒
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //输出
                System.out.println(Thread.currentThread().getName() + "-->" + num.getI());
                num.insertnum(num.getI());
                //唤醒线程2
                num.notify();
            }
        }
    }
}

//线程2

class TwoThread extends Thread{
    private Number num;
    public TwoThread(Number num){
        this.num=num;
    }
    public void run(){
        synchronized (num) {
            while (true) {
                //判断是否是奇数。若是线程进入无限期等待
                if (num.getI() % 2 != 0) {
                    try {
                        num.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //走到这里说明i为偶数,先睡2s
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //输出
                System.out.println(Thread.currentThread().getName() + "-->" + num.getI());
                //调用累加方法
                num.insertnum(num.getI());
                //唤醒线程1
                num.notify();
            }
        }
    }
}

//主线程

public static void main(String[] args) {
    //Number对象
    Number n=new Number(0);
    //两个线程共享一个对象
    Thread t1=new OneThread(n);
    Thread t2=new TwoThread(n);
    t1.setName("t1");
    t2.setName("t2");
    t1.start();
    t2.start();
​
}

5、反射机制

1>反射机制有什么用?

通过java语言中的反射机制可以操作字节码文件

2>反射机制的相关类在哪个包下?

java.lang.reflect.*;

3>反射机制相关的重要的类有哪些?

java.lang.Class:代表整个字节码

java.lang.reflect.Method:代表字节码中的方法字节码

java.lang.reflect.Constructor:代表字节码中的构造方法

java.lang.reflect.Field:代表字节码中的属性字节码

6、获取字节码的三种方式

  • 第一种方式 Class.forName("完整类名带包名");
  • 第二种方式 Class c1=对象.getClass();

  • 第三种方式 Class c2=类型.class;

 

public static void main(String[] args) {
    //第一种方式 Class.forName("完整类名带包名");
    Class c1=null;
    try {
         c1=Class.forName("java.lang.String");//c1代表String.class文件
        Class c2=Class.forName("java.util.Date");//c2代表Date.class文件
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    //java语言中任何一个对象都有一个方法:getClass()
    String s="ads";
    Class x=s.getClass();//x代表String.Class字节码文件,x代表String类型
    System.out.println(x==c1);//true 说明x与c1指向的是同一份String字节码文件
    //第三种方式:java语言中任何一种类型都有.class属性
    Class y=int.class;
    Class z=String.class;
    System.out.println(z==x);//true
}

7、关于Class的forName方法

//再此回忆一下静态代码块

class MyClass {
    //静态代码块,在类加载的时候执行,只执行一次
    static {
        System.out.println("静态代码块执行了!");
    }
}

//forName

public static void main(String[] args) {
   try {
        //当只需要执行一个类的静态代码块,其他代码不需要执行,则就可以采用如下方式
        //Class.forName(“完整类名”);这个方法执行会导致类的加载,类加载静态代码块执行
        Class c1=Class.forName("TWentyFourDay.MyClass");
        System.out.println(c1);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}

8、以反射机制创建对象

newInstance()//new一个新对象 已过时

public static void main(String[] args) {
    //不以反射机制创建对象 这种是写死的
    User u1=new User();
    System.out.println(u1);//TWentyFourDay.User@6e8dacdf
    //通过获取字节码文件(反射机制)创建对象,这种方式更加灵活
    try {
        Class c1= Class.forName("TWentyFourDay.User");
        //重点:newInstance会调用无参构造方法去完成对象的创建。无参构造必须存在!
        //如果User只有有参构造,没有无参构造,则会在newInstance创建对象的时候出现InstantiationException实例化异常
        Object o = c1.newInstance();
        System.out.println(o);//TWentyFourDay.User@6e8dacdf
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

9、验证反射机制的灵活性

通过属性文档读取数据进行创建对象 验证反射机制的灵活性

java代码不用改变,就可以创建不同对象,修改配置文件即可

public static void main(String[] args) {
    try {
        //创建流
        FileInputStream file=new FileInputStream("java基础语法/text.properties");
        //map集合
        Properties list=new Properties();
        //加载
        list.load(file);
        //流关闭
        file.close();
        //从属性文件中以键的形式获取值(在文件中有“username=TWentyFourDay.User”)
        String path=list.getProperty("username");
        Class c=Class.forName(path);
        Object o = c.newInstance();//这里相当于是 User o=new User();
        //如果文件中是“username=java.util.Date”,则相当于这里是 Date o=new Date();
        System.out.println(o);//TWentyFourDay.User@7a79be86 (Sat Jul 17 19:14:13 CST 2021)
​
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}

10、通用的路写法

一种比较通用的路径写法,即使跨操作系统,代码位置改变,依旧可以。 但是这种方式的前提是,文件必须在类路径下,类路径指的是在src下 src是类的根路径(不准确,可以这样理解)

String path=Thread.currentThread().getContextClassLoader().getResource("tempfile03").getPath();
System.out.println(path);//返回的是文件的绝对路径

对路径代码的解释:

Thread.currentThread():当前线程

getContextClassLoader():线程对象的方法,可以获得当前线程的类加载器对象

getResource(""):这是类加载器对象的方法,当前线程的类加载器默认从类的根路径下加载资源

Thread.currentThread().getContextClassLoader().getResource("tempfile03")

posted @ 2021-07-20 11:43  别不开心,过得去  阅读(53)  评论(0)    收藏  举报