Java学习(第九日)

序列化;反序列化

序列化

import java.io.*;

public class EasySerVersion {
    public static void writeStudent(Student stu) {
        FileOutputStream fos = null;
        ObjectOutputStream oos = null;
        File file = new File("D:\\student.date");
        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();

        }catch(IOException e){
            e.printStackTrace();
        }finally{
            if(oos!=null){

                try{
                    oos.close();
                }catch(IOException e){
                    e.printStackTrace();
                }
            }
            if(fos!=null){
                try{
                    fos.close();
                }catch(IOException e){
                    e.printStackTrace();
                }
            }

        }
    }
}

反序列化

反序列化的对象是一个新的对象

import java.io.*;

public class EasySerVersion {
    //序列化版本号

    public static Student readStudent(){
        File file = new File("D:\\student.date");
        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();
                }
            }
        }
    }
}

Student类

serialVersionUID 是一个类的序列化版本号,如果该量没有定义,JDK会自动给予一个版本号。

当前类发生变化时 ,序列化版本号会发生变化,反序列化就会失败 。

       自定义该版本号,只要该版本号不发生变化,即使类中属性/方法改变,该类的对象依旧可以反序列化。

private static final long serialVersionUID=1L;

       transient关键字的作用是,在对象序列化过程中,用该关键字修饰的实例变量不会被序列化。换句话说,该实例变量在对象的序列化的过程中会丢失,即序列化后的对象无法获取该实例变量的值。

private transient String sex;
//禁止属性的值被序列化
import java.io.Serial;
import java.io.Serializable;

public class Student implements Serializable {
    @Serial
    private static final long serialVersionUID=1L;
    private transient Teacher tea=new Teacher();
    //private String name;
    private transient String sex;
    //禁止属性的值被序列化
    private double score;
    private String code;
    public void test(){}
    @Override
    public String toString() {
        return "Student{" +
                //"name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", score=" + score +
                ", code=" + code +
                '}';
    }
    public Student() {

    }
    public Student(String name, String sex, double score) {
        //this.name = name;
        this.sex = sex;
        this.score = score;
    }
    //public String getName() {
    //    return name;
    //}
    //
    //public void setName(String name) {
    //    this.name = name;
    //}
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    }
    public double getScore() {
        return score;
    }
    public void setScore(double score) {
        this.score = score;
    }
}

线程

程序运行阶段的不同运行路线

线程的生命周期:

线程类   Thread

自定义线程继承Thread类

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

实例化线程对象

Thread a=new ThreadA();
常用方法
开启线程
a.start();
休眠
Thread.sleep(5000);//5000毫秒

sleep是一个Thread类的静态方法,让运行到该行代码的线程休眠5秒,休眠后会自动启动线程

获取当前线程对象
Thread.currentThread()
设置优先级
a.setPriority(4);

       优先级越高,获取CPU资源的几率越大。优先级是从小到大是1—10,默认为5,设置其他值会报错

礼让
Thread.yield();

       让出CPU资源,让CPU重新分配,防止一条线程长时间占用CPU资源,达到CPU资源合理分配的效果(.sleep(0)也可以达到上面效果)

加入(插队)
B.join();

B线程运行完毕后,A线程再运行。join方法与yield方法不同,join会完全让出CPU资源。

关闭线程

1.执行 .stop() 方法(方法已被删除,不推荐)

a.stop();

2.调用 interrupt() 设置中断状态,这个线程不会中断,我们需要在线程内部判断中断状态是否被设置,然后执行中断操作。

a.interrupt();
class ThreadF extends Thread{
    @Override
    public void run() {
        for(int i=0;i<10000;i++){
            if (Thread.currentThread().isInterrupted()){
                break;
            }
            System.out.println(i);
        }
    }
}

3.自定义一个状态属性,在线程外部设置此属性,影响线程内部的运行

class ThreadG extends Thread{
    volatile boolean stop=false;
    @Override
    public void run() {
        while(!stop){
            System.out.println("A");
        }
    }
}

线程安全

线程安全

       多个线程操作一个对象,不会出现结果错乱的情况(数据丢失,缺失);StringBuffer就是线程安全的。

public class EasySyncThreadA {
    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) {
            throw new RuntimeException(e);
        }
        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");
        }
    }
}

线程不安全

       多个线程操作一个对象,会出现结果错乱的情况(数据丢失,缺失);StringBuilder就是线程不安全的。

public class EasySyncThreadA {
    public static void main(String[] args) {
        StringBuilder strB=new StringBuilder();
        //线程可以执行的任务
        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) {
            throw new RuntimeException(e);
        }
        System.out.println(strB.length());
    }
}
class RunA implements Runnable {
    StringBuilder strB;
    public RunA(StringBuilder strB) {
        this.strB = strB;
    }
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            strB.append("0");
        }
    }
}

synchronized关键字

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

public class SyncThreadB {
    public static synchronized void test() {
        try {
            System.out.println("----进入方法"+Thread.currentThread().getName());
            Thread.sleep(1000);
            System.out.println("----执行完毕"+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    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) {
                throw new RuntimeException(e);
            }
            System.out.println("结束同步代码块"+Thread.currentThread().getName());
        }
    }
    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 {
    public void run() {
        SyncThreadB.testA();
    }
}

运行结果

使用synchronized需要指定锁对象
锁类
synchronized(SyncThreadB.class)
锁方法 
public synchronized static void method() {
   
}
锁的分类
根据有无锁对象分为悲观锁和乐观锁

悲观锁有锁对象 ,乐观锁没有锁对象;synchronized 是悲观锁

乐观锁实现方式CAS和版本号控制

      使用CAS(Compare-And-Swap)操作实现乐观锁。以下是一个简单的乐观锁实现示例:

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;
 
public class OptimisticLockExample {
    // 使用AtomicInteger实现乐观锁
    private AtomicInteger value = new AtomicInteger(0);
    // 使用AtomicStampedReference实现带有时间戳的乐观锁
    private AtomicStampedReference<Integer> valueWithStamp = new AtomicStampedReference<>(0, 0);
    // 获取值的方法
    public int getValue() {
        return value.get();
    }
    // 设置值的方法
    public void setValue(int newValue) {
        while (true) {
            int currentValue = value.get();
            if (value.compareAndSet(currentValue, newValue)) {
                // CAS成功,退出循环
                break;
            }
            // CAS失败,继续尝试
        }
    }
    // 带有时间戳的乐观锁设置值的方法
    public void setValueWithStamp(int newValue) {
        int[] stamp = new int[1];
        int[] value = new int[1];
        while (true) {
            // 获取当前值和时间戳
            value[0] = this.valueWithStamp.get(stamp);
            int currentStamp = stamp[0];
            // 尝试更新值
            if (this.valueWithStamp.compareAndSet(value[0], newValue, currentStamp, currentStamp + 1)) {
                // CAS成功,退出循环
                break;
            }
            // CAS失败,继续尝试
        }
    }
    public static void main(String[] args) {
        OptimisticLockExample example = new OptimisticLockExample();
        example.setValue(10);
        System.out.println("Value: " + example.getValue());
        example.setValueWithStamp(20);
        System.out.println("Value with timestamp: " + example.getValue());
    }
}

这个示例中,AtomicInteger用于实现一个简单的乐观锁,而AtomicStampedReference用于实现带有时间戳的乐观锁,以防止ABA问题。在设置值的过程中,如果CAS操作失败,线程将继续尝试,直到操作成功为止。 

在对象中使用版本号实现乐观锁:

public class OptimisticLockExample {
    private int id;
    private String data;
    private int version; // 版本号
    // 构造函数、getter 和 setter 省略
    // 模拟更新数据的方法,乐观锁实现
    public boolean updateData(int id, String newData, int expectedVersion) {
        if (this.id == id && this.version == expectedVersion) {
            this.data = newData;
            this.version++; // 更新版本号
            // 这里可以执行数据库更新操作,示例中省略
            return true;
        }
        return false;
    }
    // 示例用法
    public static void main(String[] args) {
        OptimisticLockExample record = new OptimisticLockExample();
        record.id = 1;
        record.data = "initial data";
        record.version = 1;
        // 尝试更新数据,期望版本号为1
        boolean updated = record.updateData(1, "updated data", 1);
        if (updated) {
            System.out.println("Record updated successfully");
        } else {
            System.out.println("Optimistic lock failure. " +
                "Another transaction must have changed the data.");
        }
    }
}

       在这个例子中,updateData 方法检查传入的版本号是否与对象的当前版本号匹配。如果匹配,更新数据,并将版本号递增。如果不匹配,方法返回 false,表示更新失败,因为有其他并发事务已经修改了数据。这个简单的例子展示了乐观锁的基本思想,但在实际的数据库环境中,乐观锁的实现通常会涉及数据库的特定机制,如使用 version 字段和 UPDATE 语句的条件检查

根据公平度不一样分为公平锁和非公平锁

公平锁就是先来后到  java里都是非公平锁

可重入锁 :在同步代码块中遇到相同的锁对象同步代码块,不需要再获取锁对象的权限,直接进入执行;java全都是可重入锁
根据线程状态不同分为偏向锁,轻量级锁(自旋锁),重量级锁;synchronized是偏向锁

Java中volatile关键字

volatile 是 Java 中的一个关键字,它的主要作用是用来确保变量的可见性阻止指令重排序

可见性:当一个共享变量被 volatile 修饰后,它会保证修改的值会立即被更新到主内存中,且每次使用该变量时,都会从主内存中重新读取最新的值。这就解决了多线程环境下,一个线程修改了变量,而其他线程不知道的问题。

指令重排序:Java 中的 volatile 还可以防止 JVM 的指令重排序优化。这意味着在 volatile 变量之前的所有操作都会在其之前执行,在 volatile 变量之后的所有操作都会在其之后执行。

下面是一个简单的使用 volatile 的例子:

public class VolatileTest {
    volatile boolean flag = false;
 
    public void changeFlag() {
        flag = true;
    }
 
    public void checkFlag() {
        while (!flag) {
            // do nothing
        }
        // 处理其他逻辑
    }
}

      在这个例子中,flag 变量被 volatile 关键字修饰,确保了当 flag 被修改后,其他线程能立即看到最新的值。checkFlag 方法会一直循环直到 flag 为 true。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值