JAVA学习7-对象流、多线程

61.对象流ObjectOutputStream与ObjectInputStream

将java对象存储到文件中,将java中的对象保存下来的过程我们称之为序列化(Serialize);

将硬盘中的数据恢复到内存中,恢复成java对象,我们称之为反序列化(DeSerialize);

序列化: 

public static void test20() throws Exception {
        Movie m = new Movie();
        ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("C:\\Users\\Administrator\\Desktop\\临时\\dos"));
        oos.writeObject(m);
        oos.flush();
        oos.close();
}

反序列化:

public static void test21() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("C:\\Users\\Administrator\\Desktop\\临时\\dos"));
        Object m = ois.readObject();
        System.out.println(m);
        ois.close();

}

一次序列化多个对象:

ArrayList与集合中的元素都要实现Serializable接口;

参与序列号与反序列化的类必须实现Serializable接口;

public static void test22() throws FileNotFoundException, IOException {
        // 将对象放到集合当中,序列化集合
        ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("C:\\Users\\Administrator\\Desktop\\临时\\oos"));
        List<Movie> ls = new ArrayList<>();
        ls.add(new Movie(12.2, "神秘代码"));
        ls.add(new Movie(22.2, "后天"));
        ls.add(new Movie(33.18, "2012"));
        oos.writeObject(ls);
        oos.flush();
        oos.close();
}

 反序列化集合:

public static void test23() throws Exception {
        ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("C:\\Users\\Administrator\\Desktop\\临时\\oos"));
        List<Movie> ls = (List<Movie>) ois.readObject();
        for (Movie movie : ls) {
            System.out.println(movie);
        }
}
public class Movie implements Comparable<Movie>, Serializable {}

通过查看源代码发现,Serializable接口是一个标志接口,里面没有任何内容;这个标志性接口起到一个标志的作用,java虚拟机看到一个类实现了这个接口,就会为该类自动生成一个 序列化版本号;

public interface Serializable {
}

如果希望类的某个属性不参与序列化可以用transient修饰,表示游离的,不参与序列化

transient double price;

java语言是通过两种方式区分类的:

        ①通过类名区分,如果类名不一样,那么两个类肯定是不同的;

        ②如果类名一样,通过序列化版本号区分;

如果两个人都写了同一个类,他们的类名称相同,都实现了序列化,那么这个时候java虚拟机就可以通过序列化区分这两个类;

自动生成序列化版本号的缺点:一个实现了序列化的类,一旦写入文件后,如果后续修改了类的源代码,编译后就会生成新的序列化版本号,此时这个类需要重新写入文件,否则这个类的反序列化就会报错;

优化:一个类一旦实现了Serializable接口,建议给该类提供一个固定不变的版本号,这样一旦类发生了修改,版本序列化不变,java虚拟机会认为是同一个类,不用重新将这个类写入文件,反序列化也仍然能运行;

建议将序列化版本号手动写出来,不自动生成;

手动写序列号后,两个类名相同,序列化版本号又相同的情况概率极低,可以忽略;

public class Movie implements Comparable<Movie>, Serializable {
    // transient游离的,不参与序列化
    double price;
    String mName;
    String mDirector;
    String mPlayer;

    private static final long serialVersionUID = 354646846846L;
}

62.IO与Properties联合运用

经常改变的数据,可以写到文件中,方便程序动态读取,例如将数据库链接写到文件中,修改的时候只需要修改文件,这样子不需要修改java源代码,不需要重新编译,不需要重启服务器,就可以动态拿到信息;

这种文件就称为配置文件;

当配置文件的格式是:

username=root
pwd=root
# #号是注释
#属性配置文件key重复的话,value会自动覆盖;
pwd=root123
#key与value直接最好不要有空格
#建议key和value直接用=号形式,key=value,不建议用冒号
port:3306

 这种格式时,我们把这种文件称为属性配置文件;

java规范中要求,属性配置文件以.properties结尾,但这不是必须的;

这种以.properties结尾的文件称为属性配置文件;

Properties是专门存放属性配置文件内容的一个类;

public static void test24() throws Exception {
        FileReader fr = new FileReader("C:\\Users\\Administrator\\Desktop\\临时\\config.txt");
        Properties p = new Properties();
        p.load(fr);
        System.out.println(p.getProperty("username"));
        System.out.println(p.getProperty("pwd"));
}

63.多线程

进程是一个应用程序(一个软件);

线程是一个进程中的执行场景/执行单元;

一个进程可以有多个线程;

对于java程序来说,当在dos命令窗口输入:java hello world,回车之后,会先启动JVM,JVM就是一个进程,JVM会先启动一个主线程调用main方法,再启动一个垃圾回收线程负责看护,回收垃圾,现在的java程序中至少有两个线程并发,一个垃圾回收线程,一个是执行main方法的主线程;

进程之间的内存独立不共享;

在java语言中,同一个进程内的线程之间的堆内存和方法区内存共享,但栈内存独立,一个线程一个栈;

假设启动10个线程,会有10个栈空间,每个栈之间互不干扰,各自独立执行,这就是多线程并发;

线程类似于火车站的售票口,多个售票口独立卖票,所以多线程并发可以提高效率;

java之所以有多线程机制,就是为了提高程序的处理效率;

使用了多线程机制之后,main方法结束了,程序有可能还没有结束,main方法结束只是主线程结束了,主栈空了,其他栈可能还在运行;

 真正的多线程并发:t1线程执行t1的,t2线程执行t2的,t1不会影响t2,t2也不会影响t1;

对于多核的CPU电脑来说,真正的多线程并发是没问题的;4核CPU表示在同一个时间点上,可以真正的有4个进程并发执行;

单核的CPU只有一个大脑,不能做到真正的多线程并发,但是可以做到给人一种多线程并发的感觉;

对于单核的CPU来说,在某一个时间点上,实际上只能处理一件事情,但由于CPU处理速度极快,多个线程之间频繁切换执行,给人的感觉是同时在做;比如播放音乐于与播放视频,CPU实际上是在这两个进程之间频繁切换的,音乐与视频一直在播放,给人的感觉是同时进行的;

java中实现多线程有2种方式:

        ①一个类直接继承java.lang,Thread,并重写run方法;

        ②A类实现Runnable接口,并重写run方法,new Thread对象传入A类的实例,线程对象调用start方法;

第二种方式更为实用,因为一个类实现了接口还可以继承其他类,更灵活;

64.继承Thread启动多线程

public class TestThread extends Thread {

    @Override
    public void run() {
        // TODO Auto-generated method stub
        //这段代码运行在分支线程中
        for (int i = 0; i < 200; i++) {
            System.out.println("支线程i-->" + i);
        }
    }
}
public class App {
    public static void main(String[] args) throws Exception {
        // main方法,这里的代码属于主线程,在主栈中运行
        // 新建一个分支线程对象
        TestThread tt = new TestThread();

        // tt.run(); //对象调用不会启动线程,不会分配新的分支栈,这种方式是单线程

        // 启动线程
        /*
         * start方法的作用是启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码完成后会瞬间结束
         * 这段代码的作用只是为了开启一个新的栈空间,只要新的栈空间开出来,start方法就结束了,线程就启动成功了
         * 启动成功的线程会自动调用run方法,并且run方法会在分支栈的栈底部(压栈)
         * run方法在分支栈的栈底部,main方法在主栈的栈底部,run和main是平级的
         */
        tt.start();
        // 这段代码运行在主线程中
        for (int i = 0; i < 200; i++) {
            System.out.println("主线程i-->" + i);
        }
    }
}

65.实现Runnable启动多线程

package Thread;

public class TestThreable implements Runnable {

    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 200; i++) {
            System.out.println("ruanble支线程i-->" + i);
        }
    }

}
public static void test26() {
        //创建线程对象
        Thread t = new Thread(new TestThreable());
        //启动多线程
        t.start();
        for (int i = 0; i < 200; i++) {
            System.out.println("主线程i-->" + i);
        }
    }

66.线程的生命周期

 67.线程的相关方法

设置与获取线程名字:

        Thread t = new Thread(new TestThreable());

        Thread t2 = new Thread(new TestThreable());
        //设置线程名字
        t2.setName("aasd");
        //获取线程名字
        System.out.println(t.getName());
        System.out.println(t2.getName());

当没有给线程设置名字的时候,线程的默认名字是 Thread-0,Thread-1...

public static void main(String[] args) throws Exception {
        Thread t = Thread.currentThread();
        //获取当前线程的名字, 在main方法中执行,获取的就是主线程
        System.out.println(t.getName());
        test26();
    }

获取当前线程:

public static void main(String[] args) throws Exception {
        Thread t = Thread.currentThread();
        // 获取当前线程的名字, 在main方法中执行,获取的就是主线程
        System.out.println(t.getName());
        test26();
    }
public static void test26() {
        // 创建线程对象
        Thread t = new Thread(new TestThreable());
        Thread t2 = new Thread(new TestThreable());
        t.setName("支线程007");
        t2.setName("X线程XXX");
        // 启动多线程
        t.start();
        t2.start();
    }
@Override
    public void run() {
        Thread t = Thread.currentThread();
        String n = t.getName();
        // TODO Auto-generated method stub
        for (int i = 0; i < 100; i++) {
            System.out.println(n + "i-->" + i);
        }
    }

68.sleep方法

static void sleep(long mis)

作用:让当前线程进入休眠,进入阻塞状态,放弃占有CPU时间片,让给其他线程使用;

        // 当前线程睡眠3秒
        Thread.sleep(3000);
        System.out.println("helll");

sleep方法可以实现一段程序几秒后运行,或者每隔几秒运行一次;

中断睡眠:

public static void test26() {      
          Thread t = new Thread(new TestThreable());
        // 启动多线程
        t.start();        
         // 中断t线程的睡眠,这种中断睡眠的方法依靠java的异常处理机制
        t.interrupt();
}

public class TestThreable implements Runnable {

    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 30; i++) {
            System.out.println("i-->" + i);
            try {
                Thread.sleep(1 * 1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

69.终止线程

stop:

public static void test26() {
        Thread t = new Thread(new TestThreable());
        // 启动多线程
        t.start();
        try {
            Thread.sleep(3 * 1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // 终止线程,已过时,不建议使用
        t.stop();
}

public class TestThreable implements Runnable {

    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 30; i++) {
            System.out.println("i-->" + i);
            try {
                Thread.sleep(1 * 1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

缺点:容易丢失数据,因为这种方式直接将线程杀死,线程没有保存的数据容易丢失,不建议使用;

合理的终止一个线程:

在线程类内部设置一个flag即可

public static void test26() {
        // 创建线程对象
        TestThreable tt = new TestThreable();
        Thread t = new Thread(tt);
        // 启动多线程
        t.start();
        try {
            Thread.sleep(3 * 1000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        // 终止线程,已过时,不建议使用
        tt.flag = false;
        System.out.println("sssssssssss");
    }

public class TestThreable implements Runnable {

    public boolean flag = true;

    @Override
    public void run() {
        // TODO Auto-generated method stub
        for (int i = 0; i < 30; i++) {
            if (flag) {
                System.out.println("支线程i-->" + i);
                try {
                    Thread.sleep(1 * 1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } else {
                return;
            }
        }
    }
}

70.线程调度

常见的线程调度模型:

        抢占式调度模型:哪个线程的优先级比较高,抢到的CPU时间片的概率就高一些;JAVA使用的就是抢占式调度模型;

        均分式调度模型:平均分配CPU时间片,每个线程占用的CPU时间片时间长度一样,平均分配;有一些语言线程调度模型采用的就是这种方式;

相关方法:

        int getPriority():获取线程优先级;

        void setPriority(int newPriority):设置线程优先级;

        最大优先级:10   (优先级高的获取的CPU时间片可能会多一些,但也不完全是,大概率是多的)

        最小优先级:1

        默认优先级:5

        static void yield():暂停当前的线程对象,并执行其他线程;

        yield方法不是阻塞方法,让当前线程让我,让给其他线程使用,yield方法的执行会让当前线程从运行状态回到就绪状态,回到就绪之后有可能还会再抢到;

        void  join():当前线程进入阻塞状态,等待调用这个方法的线程结束,再继续执行当前线程;

    //yield()方法
    public static void test26() {
        // 创建线程对象
        TestThreable tt = new TestThreable();
        Thread t = new Thread(tt);
        // 启动多线程
        t.start();
        for (int i = 0; i < 200; i++) {
            if (i % 10 == 0) {
                Thread.yield();
            }
            System.out.println("主线程i-->" + i);
        }
    }

    //join方法
    public static void test26() {
        // 创建线程对象
        TestThreable tt = new TestThreable();
        Thread t = new Thread(tt);
        // 启动多线程
        t.start();
        for (int i = 1; i < 100; i++) {
            if (i % 10 == 0) {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            System.out.println("主线程i-->" + i);
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

y_w_x_k

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值