[Java] 线程和序列化

avatarplhDigital nomad

什么是线程

线程是进程的一个实体,是CPU调度和分配的基本单位,其本身不拥有系统资源,只含有程序计数器,寄存器,和栈等一些运行时不可比少的基本资源,他的存在是为进程服务的,同属一个进程的线程共享进程所拥有的全部资源。

什么是进程

进程是具有一定独立功能的程序块关于某个数据集合上的一次运行活动,他是系统进行资源调度分配的一个独立单位

什么是程序

程序是一组指令的集合,由多个进程共同完成,他是一个静态的实体,没有执行的含义。

进程和线程的区别

  1. 线程是进程的一部分,因此线程有的时候被称为轻权的进程或者轻量级进程
  2. 一个没有线程的进程是可以被看作单线程,如果一个进程内拥有多个进程,那么进程的执行过程就不是一条线,而是多条线共同完成
  3. 系统在运行的时候会为每个进程分配不同的内存区域,但不会为每个线程分配内存,线程组只能共享资源
  4. 进程的控制表和PCB类似

程序是一组指令集和,静态的,没有执行含义,进程是动态的实体,有生命周期,一个进程和一个程序对应,只有一个,但一个程序可以有多线程,或者一个进程都没有,进程还有并发性

TIP 理解概念,有助于掌握多线程开发。

2 ways to run multiple thread

/*
 * write a class, extends Thread and override run method.
 * In run method, print 1 to 10.
 * In main method, create an instance of this class and start it.
 */
public class MyThread extends Thread {

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();
    }

    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i + 1);
        }
    }
    
}

/*
 * write a class, implements Runnable and override run method.
 * In run method, print 1 to 10.
 * In main method, create an instance of this class and create a Thread with it, then start the thread.
 */
class MyThread_impl implements Runnable {

    public static void main(String[] args) {
        MyThread_impl runnable = new MyThread_impl();
        Thread thread = new Thread(runnable);
        thread.start();
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(i + 1);
        }
    }

}

线程的生命周期

  1. New 一个线程创建成功,但是没调用start()
  2. Runnable 调用了start()但是可能还没到运行梯队,例如被阻塞了,或者sleep了
  3. Running 线程唯一一种运行方式,
  4. Blocked 当线程被认为sleep,wait状态,
  5. Dead 当run执行完成后的状态,等待垃圾回收

线程优先级

一共10级,1-10,数字越大,级别越高,MIN_PROORITY = 1. NORMAL_PRIORITY = 5, MAX_PRIORITY = 10, by default is NORMAL_PRIORITY,

停止线程

public class TestStop extends Thread {
    public static void main(String[] args) {
        TestStop ts = new TestStop();
        ts.start();
    }

    @Override
    public void run() {
        int count = 0;
        while (this.isAlive()) {
            System.out.println("Running... " + count + " times" + isAlive());
            count++;
            if (count == 5) {
                this.interrupt(); // Interrupt the thread after 5 iterations
                System.out.println("Stopping the thread...");
            }
        }
    }   
}

为什么不推荐用 stop 和 suspend 停止线程

stop 是一种粗暴的终止线程行为,没有任何清除操作,因此不安全。

而suspend会导致锁死。

应该在线程里面加入isActive标志是否活跃,如果等待过长时间,可以使用 interrupt()中断等待。

如何控制线程暂停和启动

使用sleep

但是sleep只是睡眠特定的时间,再次可执行,而不是立即执行,因此不好使

使用 wait 和 notify方法

使用 yield 方法

使用 join 方法

如何实现多个线程同步

  1. 使用synchronized关键字
  2. Metux 互斥体的设计和应用
  3. ThreadLocal的设计与使用 threadlocal是线程的本地实现,但他不是线程,他是一个线程局部变量,它为每一个线程的变量提供一个变量值的副本值,每个变量都你能改变自己的副本值,就不会和其他变量冲突,它主要是为了解决多线程访问同一个变量冲突了,

Java 序列化

Serializable

class Person implements Serializable {
    private String name;
    private int age;
    private String birthDate;

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getBirthDate() {
        return birthDate;
    }
    public void setBirthDate(String birthDate) {
        this.birthDate = birthDate;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

Externalizable

class Person2 implements Externalizable {
    private String name;
    private int age;
    private Date birthDate;

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Date getBirthDate() {
        return birthDate;
    }
    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'writeExternal'");
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'readExternal'");
    }
}

Java 读取文件,写入文件

import java.io.Externalizable;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.sql.Date;

class Person implements Serializable {
    private String name;
    private int age;
    private String birthDate;

    public Person() {
    }

    public Person(String name, int age, String birthDate) {
        this.name = name;
        this.age = age;
        this.birthDate = birthDate;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getBirthDate() {
        return birthDate;
    }
    public void setBirthDate(String birthDate) {
        this.birthDate = birthDate;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

class Person2 implements Externalizable {
    private String name;
    private int age;
    private Date birthDate;

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Date getBirthDate() {
        return birthDate;
    }
    public void setBirthDate(Date birthDate) {
        this.birthDate = birthDate;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'writeExternal'");
    }
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        // TODO Auto-generated method stub
        throw new UnsupportedOperationException("Unimplemented method 'readExternal'");
    }
}

public class IO {
    public void write() throws IOException {
        FileOutputStream fos = new FileOutputStream("./p.txt", true);
        ObjectOutputStream oos = new ObjectOutputStream(fos);

        Person p = new Person("Alice", 30, "1993-05-15");
        oos.writeObject(p);
        oos.flush();
    }

    public void read() throws IOException, ClassNotFoundException {
        FileInputStream fis = new FileInputStream("./p.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        Person p = (Person) ois.readObject();
        System.out.println(p.getName() + " " + p.getAge() + " " + p.getBirthDate());
    }

    public static void main(String[] args) throws ClassNotFoundException {

        IO e = new IO( );
        try {
            // e.write();
            e.read();
        } catch (IOException ex) {
            ex.printStackTrace();
        }

    }
}

如何自定义序列化和反序列化

如果涉及到敏感信息的话,例如密码,通常需要考虑到密码泄漏问题,一般调用readObject()和writeObject()的方法实现, 而这两个方法需要重写

序列化过程中,serialVersionUID有什么作用?

作为序列化的唯一ID, 初始化的时候前面加上final,是用来做id比对的,如果id同样的,默认可以进行反序列化。