线程,定时器,绝对路径

线程

  1. 局部变量永远不会有线程安全问题,他不共享

  2. synchronizzed出现在实例方法上,一定锁的是this,这种方式不灵活,另外,synchronized出现在实力方法上,整个方法体都被同步,会扩大同步的范围,导致程序效率降低

public synchronizzed void withdraw(){}//这种synchronizzed出现在了实力方法上,锁的一定是this
​
  1. 如果使用局部变量,建议使用stringBulier(非线程安全的),因为局部变量不存在线程安全问题,这样可以提高效率

    总结:

    synchronized有三种写法

    • 第一种:同步代码块

      灵活

      synchronized(线程共享对象){

      同步代码块

      }

    • 第二种:在市里方法上使用 synchronized

      表示共享对象一定是this

      并且同步代码块是整个方法体

    • 第三种:在静态方法上使用 synchronized

      表示找类锁

      类锁永远只有一把

    死锁

    1. 产生死锁:让两个线程共享一个资源,其中一个线程先锁住o1,再让该线程睡眠1秒,此时另外一个线程也快速锁住o2,也让他睡一秒,第一个线程睡眠结束发现o2已经被锁住,总结无法锁,同样另外一个线程也是,因此他们一直僵持着,程序无法结束,一直循环

    //死锁
    public class Thread05 {
        public static void main(String[] args) {
            Object o1 = new Object();
            Object o2 = new Object();
    ​
            My my = new My(o1, o2);
            Me me = new Me(o1, o2);
              my.start();
              me.start();
        }
    }
    ​
    ​
     class My extends Thread{
           Object o1;
           Object o2;
           public My(Object o1,Object o2){
                  this.o1=o1;
                  this.o2=o2;
           }
           public void run(){
                 synchronized (o1){
                     try {
                         Thread.sleep(1000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     synchronized (o2){
    ​
                     }
                 }
           }
     }
    ​
     class Me extends Thread{
         Object o1;
         Object o2;
         public Me(Object o1,Object o2){
             this.o1=o1;
             this.o2=o2;
         }
         public void run(){
             synchronized (o2){
                 try {
                     Thread.sleep(1000);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 synchronized (o1){
    ​
                 }
             }
         }

注意!:两把锁是嵌套的才会发生如果o1一直不能执行程序一直不结束

         synchronized (o2){
       
             synchronized (o1){
​
             }
         }

线程安全问题

在开发中应该怎么解决线程安全问题:

  • 方法一;采用局部变量代替实例变量和静态变量(最好)

  • 方法二:创建多个对象,让每一个线程都有单独的对象,不共享资源(排第二)

  • 方法三:如果不能使用前面两个方法,只能采用synchronized

守护线程

  1. 什么是守护线程

    • 当main方法的主线程结束,守护线程跟着结束

  2. 创建一个守护线程

public static void main(String[] args) {
          Thread be = new BeiFen();
           be.setName("备份线程");
         //将be这个线程变成守护线程,当main方法的主线程结束,守护线程跟着结束
           be.setDaemon(true);
           be.start();
     //创建一个循环
        for (int i = 0; i < 10; i++) {
            try {
                System.out.println(Thread.currentThread().getName()+"-------->"+i);
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
​
​
        }
​
    }
}
​
class BeiFen extends Thread{
     public void run(){
         int i =0;
         //创建一个死循环
         while(true){
             try {
                 System.out.println(Thread.currentThread().getName()+"-------->"+(++i));
                   Thread.sleep(1000);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
​
         }
     }

总结:即便守护线程是一个死循环,只要主线程结束,他就跟着结束

定时器

  1. 定时器的作用

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

    • public static void main(String[] args) {
             //制作一个定时器
             Timer timer = new Timer();
            // Timer timer = new Timer(true); 这种是把定时器变成一个守护线程
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-hh HH:mm:ss");
             try {
                 Date firsttime = sdf.parse("2021-11-21 12:22:10");
                // timer.schedule(执行任务的对象 , 第一次执行的时间 , 间隔多久执行一次);
                 timer.schedule(new LongTmieTask() ,firsttime ,1000*5);
             } catch (ParseException e) {
                 e.printStackTrace();
             }
         }
      }
      //因为   // timer.schedule(执行任务的对象 , 第一次执行的时间 , 间隔多久执行一次);
      // 中必须是执行任务的对象,而 TimeTAsk是一个抽象类 无法实例化,所以创建一个子类去继承他,并重写他的抽象方法
       class LongTmieTask extends TimerTask{
      ​
          @Override
          public void run() {
              DateFormat df = new SimpleDateFormat("yyyy-MM-hh HH:mm:ss");
               String strtime = df.format(new Date());
              System.out.println(strtime+"成功备份");
          }
           
           

回忆时间的获取

public static void main(String[] args) {
        //获得当前时间
        Date time = new Date();
        //设置一个时间修改的模式
        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //采用fomat方法将当前时间修改成这个模式
             String b = s.format(time);
             //创建一个字符串数组将他打印成时间
             String a ="1999-11-07 12:22:30";
        SimpleDateFormat st = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
         Date parse = null;
        try {
            parse = st.parse(a);
            System.out.println("自己设置的时间"+parse);
            System.out.println("当前时间"+b);
        } catch (ParseException e) {
            e.printStackTrace();
        }
​
​
    }
}

wait()方法和notify()方法

  1. 第一:wait和notify方法都不是线程对象的发给发,是java中任何一个对象都有的方法,因为这两个方法是object自带的

  2. 第二:wait()方法作用

    object 0 = new object();

    o.wait();

    表示让正在被o对象活动的线程静茹等待状态,无期限的等待,直到被唤醒

    wait方法的调用会让当前线程(正在o对象上活动的线程静茹等待状态)

  3. 第三:notify()方法作用

    object 0 = new object();

    o.notify();

    表示:唤醒正在o对象上等待的线程

    还有一个notifyAll()方法:表示唤醒所有o对象上处于等待的线程

作业!

​
/*使用生产者和消费者模式实现交替输出:
* 假设只有两个线程,输出一下结果
*   t1--->1
*   t2--->2
*   t3--->3
*   t4--->4
*   t5--->5
*   t6--->6
* 要求必须交替输出,并且t1线程负责输出奇数,t2输出偶数
* 两个线程共享数字,每个线程执行时都要对这个数字加1
*    */
public class ThreadWork {
    public static void main(String[] args) {
          Num num = new Num(10);
        //创建对象
         Thread t1 = new Thread(new Productor(num));
         Thread t2 = new Thread(new Coustom(num));
         t1.setName("t1");
         t2.setName("t2");
         t1.start();
         t2.start();
​
    }
}
​
class Productor implements Runnable{
    private Num num;
​
    public Productor(Num num) {
        this.num = num;
    }
​
    @Override
    public void run() {
        while(true){
            synchronized (num){
                if (num.i%2!=0){
                    try {
                        num.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"输出的偶数数是"+num.i++);
                num.notify();
            }
        }
    }
}
​
class Coustom implements Runnable{
    private Num num;
    public  Coustom (Num num) {
        this.num = num;
    }
    @Override
    public void run() {
         while(true){
             synchronized (num){
                 if (num.i%2==0){
                     try {
                         num.wait();
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
                 try {
                     Thread.sleep(1000);
​
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
                 //输出奇数
                 System.out.println(Thread.currentThread().getName()+"输出的奇数是"+num.i++);
                 num.notify();
             }
         }
    }
}
​
class Num{
     int i ;
​
    public Num(int i) {
        this.i = i;
    }
​
}

反射

  1. 反射机制可以操控字节码

  2. 反射机制相关的重要的类

    • java.lang.class: 代表整个字节码,代表一个类型,代表整个数

    • java.lang.reflect.method: 代表字节码中的方法字节码,代表类中的方法

    • java.lang.reflect..constructor: 代表字节码中构造方法的字节码,代表类中构造方法的字节码

    • java.lang.reflect.Field:代表字节码中的属性字节码,代表类中的成员变量

  3. 反射机制是操作字节码的,所以我们应该先拿到字节码,有三种方法

    • 方法一 :Class.forName()Class首字母大写

      • 静态方法

      • 方法的参数是一个字符串

      • 字符串需要的是一个完整类名

      • 完整类名必须带有包名,java.lang包也不能省略

        Class c1 = Class.forName("java.lang.String") //c1 代表String.class文件
        Class c2 = Class.forName("java.util.Date")//c1 代表Date.class文件
        Class c3 = Class.forName("java.lang.Integer")//c1 代表Integer.class文件
        Class c4 = Class.forName("java.lang..System") //c1 代表System.class文件
        ​
    • 方法二:

    • ==比较的是内存地址,但是class文件在指挥产生一个内存地址,所以下面的比较是true

    •  Class c1 = null;
       Class c4 = null;
        c1 = Class.forName("java.lang.String");
        c4 = Class.forName("java.util.Date");
        //下面为方法二,可以得到与方法一得到的文件完全一样
      String x ="adc";
      Class e = x.getClass();
      System.out.println(e == c1);//true
      Date date = new Date();
       Class e2 = date.getClass();
      System.out.println(e2==c4);//true

方法三:

//第三种方式
Class b1 = String.class;//代表String文件
Class b2 = Date.class; //代表Date文件
Class b3= Integer.class; //代表Integer文件
System.out.println(b1==e);//true
System.out.println(b2==e2);//true

newinstance

      //通过反射来创建一个对象
        try {
            Class c = Class.forName("fanshe.User");//()里面是User类的完整类名,然后调用newinstance方法来实例化一个对象
            try {
                    Object o = c.newInstance();//完成了对象的创建,但是这种方法会调用User的无参构造,所以要注意
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
​
​
}
class User{
      //如果这里定义了有参构造,一定要把午餐构造写出,不然上面会报错
}
  1. 反射实例化对象的优势:很灵活,可以通过在文件种修改v值,那么得到的完整的类名就会不同,就会实例化不同的对象,也可以批量实例化

     public static void main(String[] args) {
    ​
            try {
                //创建io流读取文件数据
                FileReader  reader = new FileReader("FANSHE/FanSheIo.properties");
               //创建map集合
                Properties pro = new Properties();
                  //读取文件
                pro.load(reader);
                //关闭流
                reader.close();
                String username = pro.getProperty("username");//得到的是一个完整的类名,于是可以通过反射的方法实例化对象
                 Class c = Class.forName(username);
                   System.out.println(c);
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            }
    ​
        }
    }
    ​
    ​
    class Users {
       
    }

  1. 如果你只希望一个类的静态代码块执行,那么可以只写Class.forName(完整类名),这个方法会导致类加载,而静态代码快就是在类加载是执行

     public static void main(String[] args) {
    ​
                        Class.forName("fanshe.User");
         
         
     }
    class User{
           static{System.out.println("静态代码快执行了");}
    }

获取绝对路径

  1. 只有在src的根路径下的文件才可以用这种方法

    String path = Thread.currentThread().getContextClassLoader().getResource("这里写文件在src下的路径").getPath();

    public static void main(String[] args) {
        //获取文件的绝对路径 只适用于文件在src下的文件
        String path = Thread.currentThread().getContextClassLoader().getResource("fanshe/LuJing.properties").getPath();
        //System.out.println(path);
        //但是我输出的会又5% 7%这些东西,网上查了查先对path惊醒一个dvade
        path = java.net.URLDecoder.decode(path,"utf-8");
        //在输出路径就对了
        System.out.println(path);
    }

资源绑定器!

资源绑定器! 不需要用到io流:java.util.包下提供了一个资源绑定器,便于获取属性配置文件种的内容,使用一下这种方式的时候,属性配置文件xxx.properties必须放在类路径下面,

而且只能绑定xxx.properties文件.并且这个文件的扩展名一定要是properties 而且在写路径的时候properties还不用写

public static void main(String[] args) {
        //资源绑定器
        ResourceBundle  budle = ResourceBundle.getBundle("fanshe/LuJing");
        //这种查找文件种的v值就不需要创建一个流个map集合了
        String username = budle.getString("username");
        System.out.println(username);
    }
}
​
class Userss{
​
}
​

不需要创建流和map集合就可以查找到v值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值