JAVA小白自学日记Day9

 1.序列化


序列化版本号:serialVersionUID,是一个类的序列化版本号。如果在反序列化时,类的 serialVersionUID 与序列化时的版本号不匹配,那么会抛出 InvalidClassException 异常,表示类的版本不兼容,无法进行反序列化。
如果流量没有定义,JDK会自动给与一个版本号,当该类发生变化(属性和方法),该序列化版本号会发生变化,反序列化失败
如果已经自定义版本号,只要该版本号不发生变化,即使类中属性或者方法改变,该类的对象依旧可以反序列化。

transient关键字,可避免被序列化。属性值不会被序列化出去,其会是默认值。

缓冲流中的flush方法有内容,写出都有flush,写出完都要flush。

package com.easy724;

import java.io.*;

public class EasySerVersion {
    public static void main(String[] args) {
        //序列化版本号serialVersionUID   是一个类的序列化版本号
        //如果流量没有定义,JDK会自动给与一个版本号,当该类发生变化(属性和方法),该序列化版本号会发生变化,反序列化失败
        //如果已经自定义版本号,只要该版本号不发生变化,即使类中属性或者方法改变,该类的对象依旧可以反序列化

        Student stu=new Student("张三","男",99);
        writeStudent(stu);
        Student readStu=readStudent();
        System.out.println(readStu);
        //反序列化的对象是一个新对象
    }
    //序列化版本号
    public static Student readStudent(){
        File file=new File("D:\\student.data");
        FileInputStream fis=null;
        ObjectInputStream ois=null;
        try {
            fis=new FileInputStream(file);
            ois=new ObjectInputStream(fis);
            Object obj=ois.readObject();
            if (obj instanceof Student){
                return (Student)obj;
            }
            return null;
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }finally {
            if (ois!=null){
                try {
                    ois.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if (fis!=null){
                try {
                    fis.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
    public static void writeStudent(Student stu){
        FileOutputStream fos=null;
        ObjectOutputStream oos=null;
        File file=new File("D:\\student.data");
        if (!file.exists()){
            try {
                file.createNewFile();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
        try {
            fos=new FileOutputStream(file);
            oos=new ObjectOutputStream(fos);
            oos.writeObject(stu);
            oos.flush();//缓冲流中的flush有内容,写出都有flush,写出完都要flush
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if (oos!=null){
                try {
                    oos.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
            if (fos!=null){
                try {
                    oos.close();
                }catch (IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

2.线程

线程(Thread):程序运行阶段不同的运行路线
自定义线程  继承  Thread

常用方法:

1.start():启动线程,当多线程同时启动时,先运行的线程先启动,但是各线程会交叉运行。

2.run():要在继承线程(Thread)的类中重写run()方法,定义线程要执行的任务,当main方法中连续出现run()方法,就是按顺序执行,不存在交叉,是一个线程。与重写的不一样。

3.sleep():休眠方法。sleep是Thread类的静态方法,让运行到此处的代码线程休眠一定时间(以毫秒为单位)。休眠后会自动启动线程。

4.Thread.currentThread():获取当前线程对象。

5.yeild():礼让。让出cpu资源,让cpu重新分配。防止一条线程长时间占用cpu资源,达到cpu资源合理分配的效果。【sleep(0)也可以达到该效果,但是表示的阶段不同】

6.join():加入(插队)。比如在A线程中执行了B.join(),B线程运行完毕后,A线程再运行。

7.priority():设置优先级。优先级越高,获得cpu资源的几率越大。优先级范围1~10,默认5,设置其他值会报错。

关闭方法:
1.执行stop方法(不推荐)。
2.调用interrupt()方法,设置中断状态,这个先不回中断,我们需要在线程内部判断,中断状态是否被设置,然后执行中断操作。isInterrupted()是判断中断状态。
3.自定义一个状态属性,在线程外部设置此属性,然后影响线程内部的运行。

 线程状态:

1) 新建

当用new关键字创建一个线程时,还没调用start 就是新建状态。

2) 就绪

调用了 start 方法之后,线程就进入了就绪阶段。此时,线程不会立即执行run方法,需要等待获取CPU资源。

3) 运行

当线程获得CPU时间片后,就会进入运行状态,开始执行run方法。

4) 阻塞

当遇到以下几种情况,线程会从运行状态进入到阻塞状态。

  • 调用sleep方法,使线程睡眠。

  • 调用wait方法,使线程进入等待。

  • 当线程去获取同步锁的时候,锁正在被其他线程持有。

需要注意的是,阻塞状态只能进入就绪状态,不能直接进入运行状态。因为,从就绪状态到运行状态的切换是不受线程自己控制的,而是由线程调度器所决定。只有当线程获得了CPU时间片之后,才会进入运行状态。(也就是等待阶段)

5) 终止

当run方法正常执行结束时,或者由于某种原因抛出异常都会使线程进入终止状态。另外,直接调用stop方法也会停止线程。但是,此方法已经被弃用,不推荐使用。

注意:当使用yield方法或者失去cpu资源时,将从运行阶段返回到就绪阶段,而使用sleep方法则是进入等待阶段。

volatile关键字的作用
1.可见性:
当一个线程修改了volatile修饰的变量的值,其他线程可以立即看到这个修改,保证了共享变量的可见性。

2.禁止指令重排序:
编译器和处理器在编译和执行代码时,可能会对指令进行重排序,但是volatile关键字可以禁止这种重排序,保证了程序的正确性。

3.保证原子性:
volatile关键字可以保证一些简单的操作的原子性,例如++操作,但是对于复合操作,volatile关键字无法保证原子性。

二、volatile关键字和synchronized关键字的区别
1.volatile关键字保证了共享变量的可见性和禁止指令重排序,但是无法保证原子性,synchronized关键字可以保证原子性、有序性和可见性。

2.volatile关键字适用于一些简单的操作,例如++操作,而synchronized关键字适用于复合操作。

3.volatile关键字不会造成线程阻塞,而synchronized关键字可能会造成线程阻塞。

代码:

package com.easy724.thread;

public class EasyThreadA {
    //线程
    //程序运行阶段不同的运行路线
    //Thread
    //自定义线程  继承  Thread
    public static void main(String[] args) {
        //实例化线程对象
        Thread a=new ThreadA();
        Thread b=new ThreadA();
        //开启线程
        a.start();//启动线程,先a线程后b线程,两个线程可能会交叉运行
        b.start();

        a.run();//main线程,跟b.run()是按顺序进行,不存在交叉,是一个线程
        b.run();
    }
}

class ThreadA extends Thread{
    //重写run方法,定义线程要执行的任务
    @Override
    public void run(){
        for (int i = 0; i <21 ; i++) {
            System.out.print(i+this.getName()+" ");
        }
    }
}
package com.easy724.thread;

public class EasyThreadB {
    //线程常用的方法

    //sleep  休眠方法
    public static void threadSleep() throws InterruptedException {
        //sleep是一个Thread类的静态方法
        System.out.println("1--------");
        //让运行到该行代码的线程休眠5秒
        //休眠后会自动启动线程
        Thread.sleep(1000*5);
        System.out.println("2--------");
    }

    public static void threadBSleep(){
        Thread t=new ThreadB();
        t.start();
    }

    //获取当前线程对象
    //Thread.currentThread()
    public static void current(){
        System.out.println(Thread.currentThread().getName());
    }
    //设置优先级
    public static void priority(){
        Thread a=new ThreadB();
        Thread b=new ThreadB();
        //设置优先级
        a.setPriority(4);
        b.setPriority(6);
        //优先级越高,获取CPU资源的几率越大
        //优先级  1~10,默认5
        //设置其他值报错
        a.start();
        b.start();
    }
    //礼让  yeild
    //作用:让出cpu资源,让cpu重新分配
    //防止一条线程长时间占用cpu资源,达到cpu资源合理分配的效果
    //sleep(0)也可以达到该效果
    public static void threadYeild(){
        Thread a=new ThreadC();
        Thread b=new ThreadC();
        a.start();
        b.start();
    }
    //join()  成员方法   加入(插队)
    //在A线程中执行了B.join()   B线程运行完毕后,A线程再运行
    public static void threadJoin(){
        Thread a=new ThreadD();
        Thread b=new ThreadD(a);
        a.start();
        b.start();
    }
    public static void main(String[] args) throws Exception {
        //threadSleep();
        //threadBSleep();
        //current();
        //priority();
        //threadYeild();
        threadJoin();
    }
}

class ThreadB extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <21 ; i++) {
            if (i%8==0){
                try {
                    Thread.sleep(1000*2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(i+this.getName());
        }
    }
}
class ThreadC extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <21 ; i++) {
            if (i%3==0){
                Thread.yield();
            }
            System.out.println(i+this.getName());
        }
    }
}
class ThreadD extends Thread{
    public ThreadD(Thread t){
        this.t=t;
    }
    public ThreadD(){

    }
    private Thread t;
    @Override
    public void run() {
        for (int i = 0; i <2000; i++) {
            if (i==10&&t!=null&&t.isAlive()){
                System.out.println(this.getName()+"执行Join");
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(i+this.getName());
        }
    }
}
package com.easy724.thread;

public class EasyThreadC {
    public static void threadstop()  {
        Thread a=new ThreadE();
        a.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        a.stop();
    }
    public static void threadInterrupted(){
        Thread a=new ThreadF();
        a.start();
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        a.interrupt();
    }
    public static void stopThread(){
        Thread a=new ThreadG();
        a.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ((ThreadG) a).stop=true;
    }
    public static void main(String[] args) {
        //threadstop();
        //threadInterrupted();
        stopThread();
    }
}

//关闭线程
//1.执行stop方法(不推荐)
//2.调用interrupt()方法,设置中断状态,这个先不回中断,我们需要在线程内部判断,中断状态是否被设置,然后执行中断操作
//3.自定义一个状态属性,在线程外部设置此属性,然后影响线程内部的运行
class ThreadE extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i);
        }
    }
}
class ThreadF extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            if (Thread.currentThread().isInterrupted()){
                break;
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i);
        }
    }
}
class ThreadG extends Thread{
    volatile boolean stop=false;

    @Override
    public void run() {
        while (!stop){
            //System.out.println("A");
        }
    }
}

3.线程安全

线程安全:多个线程操作一个对象,不会出现结果错乱的情况(数据缺失)。
线程不安全:StringBuilder就是线程不安全。
要做到线程安全,我们可以使用synchronized对方法或者代码块加锁,达到线程同步的效果。
线程同步:一个线程一个线程的执行,不能同时执行。
使用synchronized关键字修饰的代码块,同一时间内,只允许一个线程执行此代码。

使用Synchronizated需要指定锁对象。
synchronized修饰方法   1.成员方法   用this  2.静态方法   用类的类对象   obj.getClass()

锁的分类:
根据有无锁对象:悲观锁(有锁对象)和乐观锁(无锁对象)。
synchronized是悲观锁,乐观锁的实现方式:CAS和版本号控制。
根据公平度:公平锁和非公平锁。公平锁就是先来后到,非公平锁。
可重入锁:在同步代码块中遇到相同的锁对象的同步代码块,不需要再获取锁对象的权限,直接进入执行。
根据线程状态不同:偏向锁,轻量级锁(自旋锁),重量级锁。
synchronized是什么锁,这是相对的。

CAS和版本号控制:

悲观锁,认为所有线程都会对数据进行修改,所以某个线程拿数据的时候,就会上锁阻塞其余所有线程,直到锁的释放,然后其余线程继续竞争这个锁,往复循环。

乐观锁,总是认为最好的情况,每次去拿数据都认为别人不会修改,所以不会上锁,但是在更新的时候会判断在此期间别人有没有去更新这个数据,实现技术就是CAS算法和版本号机制。

CAS操作分为三部分:

1、读取内存值。

2、比较内存值与期望值是否相等。

3、如果相等,新值写入内存,否则此次操作无效。

注意(ABA问题):CAS只能对比值是否相同,但不能确定这个值是否中间发生过改变。如果对比值相等,但值之后发生改变会出现问题。

版本号控制:

为了解决CAS的ABA问题,引出了版本号机制,在数据表中加上一个version版本号的字段,表示数据被修改的次数,这个值只能增加,不能减小,当某个线程要更新数据值时,在读取数据的同时也会读取version值,若读取的version值为当前数据库中的version值相等时,才会更新数据值,否则认为这个数据已被修改,则不做任何操作。

代码:

package com.easy724.thread;

public class SyncThreadA {
    //线程安全:
    //多个线程操作一个对象,不会出现结果错乱的情况(数据缺失)
    //线程不安全:
    //StringBuilder就是线程不安全
    public static void main(String[] args) {
        StringBuffer strB=new StringBuffer();
        //线程可以执行的任务
        RunA r=new RunA(strB);
        Thread a=new Thread(r);
        a.start();
        Thread b=new Thread(r);
        b.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
             e.printStackTrace();
        }
        System.out.println(strB.length());
    }
}

//实现Runnable接口
class RunA implements Runnable{
    StringBuffer strB;
    public RunA(StringBuffer strB){
        this.strB=strB;
    }
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            strB.append("0");
        }
    }
}
package com.easy724.thread;

public class SyncThreadB {
    //要做到线程安全,我们可以使用synchronized对方法或者代码块加锁,达到线程同步的效果
    //线程同步:一个线程一个线程的执行,不能同时执行
    //使用synchronized关键字修饰的代码块,同一时间内,只允许一个线程执行此代码

    //修饰方法
    public static synchronized void test(){
        try {
            System.out.println("----------进入方法"+Thread.currentThread().getName());
            Thread.sleep(1000);
            System.out.println("----------执行完毕"+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //修饰代码块
    public static void testA(){
        System.out.println("++++++++++++进入方法"+Thread.currentThread().getName());
        synchronized (SyncThreadB.class){
            System.out.println("进入同步代码块"+Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("结束同步代码块"+Thread.currentThread().getName());
        }
    }

    //使用Synchronizated需要指定锁对象
    //synchronized修饰方法   成员方法   this
    //                       静态方法   类的类对象   obj.getClass()

    //锁的分类:
    //根据有无锁对象:悲观锁(有锁对象)和乐观锁(无锁对象)
    //synchronized是悲观锁,乐观锁的实现方式:CAS和版本号控制
    //根据公平度:公平锁和非公平锁。公平锁就是先来后到,非公平锁
    //可重入锁:在同步代码块中遇到相同的锁对象的同步代码块,不需要再获取锁对象的权限,直接进入执行
    //根据线程状态不同:偏向锁,轻量级锁(自旋锁),重量级锁。
    //synchronized是什么锁,这是相对的。
    public static void main(String[] args) {
        Runnable r=new RunB();
        Thread a=new Thread(r);
        Thread b=new Thread(r);
        a.start();
        b.start();
    }
}
class RunB implements Runnable{
    @Override
    public void run() {
        SyncThreadB.testA();
    }
}

4.BIO,AIO,NIO

java BIO:同步并阻塞

在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。

服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

java NIO:同步非阻塞

在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。

服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

java AIO:异步非阻塞

在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。

服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是有OS先完成了再通知服务器应用去启动线程进行处理。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值