首页 > 编程语言 >JAVA小白自学日记Day9

JAVA小白自学日记Day9

时间:2024-07-24 20:00:25浏览次数:19  
标签:JAVA Thread Day9 版本号 void 线程 new 自学 public

 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先完成了再通知服务器应用去启动线程进行处理。

标签:JAVA,Thread,Day9,版本号,void,线程,new,自学,public
From: https://blog.csdn.net/m0_63367052/article/details/140667720

相关文章

  • java连接redis和基础操作命令
    引入依赖<!--引入java连接redis的驱动--><dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>4.3.1</version></dependency>单机模式连接redismain(){//连接redis的信息默......
  • java中的一些经典算法code
    //1.importjava.util.LinkedList;importjava.util.Queue;publicclassCandyGame{//定义一个点的类,用于记录位置和当前累计的糖果数量staticclassPoint{intx,y,steps,candies;Point(intx,inty,intsteps,intcandies){......
  • Java后端开发知识点积累20240724
    1.使用流(Stream)API和lambda表达式来从一个dateBaseList列表中提取所有的title字段,并将这些title值收集到一个新的列表中dateBaseList.stream().map(InspectionManageEntity::getTitle).collect(Collectors.toList());2.@PathVariable注解作用@PathVariable是Spring框架中的......
  • Java学习 - Springboot 集成 Security 入门小实例
    前言SpringSecurity是Spring家族中一个强大可定制的身份验证和访问控制框架,和Shiro一样,它们都具有认证、授权、加密等用于权限管理的功能。但相比于Shiro,SpringSecurity的功能无疑更加强大。而且作为Spring家族中的一份子,配合家族中的其它兄弟-SpringBoot、S......
  • Java内存模型全解析:解决共享变量可见性与指令重排难题
    本期说一下Java内存模型(JavaMemoryModel,JMM)及共享变量可见性问题。“以下内容出自本人整理的面试秘籍。点击此处,无套路免费获取面试秘籍JMM是什么?答:Java内存模型(JavaMemoryModel,JMM)抽象了线程和主内存之间的关系就比如说线程之间的共享变量必须存储在主内存......
  • Java基础——String/StringBuilder/StringBuffer区别
    四个方面:不可变性、线程安全、性能、使用场景String:不可变,线程安全,适用于多线程编程。注意:由于String内部字符数组由final修饰,对其进行改变时会创建新的String对象,旧的会被JVM回收,容易触发gc(垃圾回收),这种行为可能会导致频繁的内存分配和垃圾回收,从而引起系统的内存抖动(memor......
  • java基础语法
    Java基础语法编译与运行在终端运行编译javajava文件,会生成一个class文件运行class文件,javaclass文件publicclassHello{ //类(publicclass)+名称(Hello)publicstaticvoidmain(String[]args){ //修饰符/关键字(publicstaticvoid)+方法(main)+参数S......
  • Java中string对象是如何实现的?string对象的优化过程
    1.基本实现Java中的String类是一个final类,这意味着它不能被继承。它内部使用一个字符数组(char[])来存储实际的字符序列。这个字符数组是私有的,并且不能被外部直接访问或修改(除了通过String类提供的公共方法)。String类还包含一些字段来跟踪字符串的长度(value.length)和哈希......
  • java代审中对jar包的审计
    在代码审计中,java比较特殊,相比于php这种纯脚本文件驱动,java还可以使用内置虚拟机驱动(如比较新的spring技术),这就常常涉及到jar包。之前都是跳过jar包,以为jar包里面就是一些框架性的东西,但是其实,有些jar包就是作者为系统专门编写或修改的,其中被引用的方法也是可能被直接引用,所以......
  • Java学习笔记(三)算术运算符、逻辑运算符、四种进制介绍
    Hii,mJinXiang⭐前言⭐本篇文章主要介绍Java算术运算符、逻辑运算符、四种进制介绍详细使用以及部分理论知识......