javaSE阶段重点知识(二)

(一)String补充:

StringBuffer:普通的String类字符串是一种常量,而StringBuffer是一种方便可以修改的字符串,有以下几种方法

  1. 构造方法
    public StringBuffer()
    public StringBuffer(int capacity)
    public StringBuffer(String str)

  2. 成员方法
    添加功能
    public StringBuffer append(String str)
    public StringBuffer insert(int offset,String str)
    删除功能
    public StringBuffer deleteCharAt(int index)
    public StringBuffer delete(int start,int end)
    替换功能
    public StringBuffer replace(int start,int end,String str)
    反转功能
    public StringBuffer reverse()

  3. String与StringBuffer之间的相互转换:
    1 StringBuffer stringBuffer = new StringBuffer(s); //String向StringBuffer转化
    2 String s1 = stringBuffer.toString(); //StringBuffer向String转化

  4. String, StringBuffer和StringBuilder有啥区别

  • 和 String 类不同的是,StringBuffer 和 StringBuilder 类的对象能够被多次的修改
    • 并且不产生新的未使用对象,不会产生效率问题和空间浪费问题
  • StringBuffer是线程安全的,StringBuilder是线程不安全的
    • StringBuilder的效率会比StringBuffer效率更高,单线程的程序推荐使用StringBuilder
    • 在多线程的程序中,应该优先考虑使用StringBuffer,安全性要更重要
    • 它们的效率都比String高很多
日期转换

在这里插入图片描述

(二)异常

  1. 根据java程序在运行过程中,根据程序运行时错误的严重程度,异常可分为:

Error:
程序层面无法处理的错误(致命的错误) 一般是jvm内部错误,比如资源耗尽之类的。
比如 : java.lang.StackOverflowError java.lang.OutOfMemoryError: Java heap space
一般默认jvm最大堆空间大小是系统的内存的1/4

Exception:
在程序中可能能够处理的错误

对于Exception,根据错误处理的方式不同
分为:
编译时异常(Checkedable Exception)

​ 可预见的,语法层面强制在代码编写时处理 除了RuntimeException 及其子类之外的异常是编译时异常

运行时异常(RuntimeException)

​ 不可预见的,不要求在编写代码时必须处理 RuntimeException 及其子类

  1. 注意:
    Exception是运行时异常还是编译时异常
  • 从继承关系上看,Exception是运行时异常 编译时异常的父类
  • 使用时(自定义异常), Exception作为编译时异常

常见异常图
在这里插入图片描述
3. jvm默认异常处理流程

①当我们代码在执行到,发生错误的地方。
② 一旦发生错误,jvm就会终止我们自己程序的运行,转而执行jvm自己的错误处理流程
③ 在发生错误地方,收集错误信息,产生一个描述错误的对象
④ 访问收集到的错误信息,将错误信息,输出到控制台窗口中

  1. 我们如果需要获取异常的信息,jvm给我们提供了以下三种方法
函数方法作用
getMessage()获取异常信息,返回字符串。
toString()获取异常类名和异常信息,返回字符串。
printStackTrace()获取异常类名和异常信息,以及异常出现在程序中的位置,并打印到控制台
  1. 使用Ctrl + Alt + T,可以包裹一段代码,使用try – catch来获取异常信息(如果没反应,请同时按下Win)
    catch也可以多分支
//从上到下依次匹配
try{
    // 可能发生异常的代码
}catch(异常类型1 对象名){
    // 对于异常的处理
}catch(异常类型2 对象名){
    // 对于异常的处理
}catch(异常类型3 对象名){
    // 对于异常的处理
}....

注意事项:
如果说,在多catch分支的情况下,如果不同的catch分支,处理的异常类型,有父子关系,那么就一定要注意,处理子类的异常分支写在前面,父类的异常分支写在后面

public static void main(String[] args) {
        try {
            System.out.println("before");
            System.out.println(10/0);
            System.out.println("after");
        }catch (ArithmeticException e){
            System.out.println("算数异常");
            String message = e.getMessage();
            System.out.println("messager = "+ message);

        }
    }
  1. 抛出异常,上层处理
    6.1 在方法定义时使用
    声明该方法可能抛出的异常
    对于编译时异常,可以在语法层面强制方法调用者处理该异常 使用throws 异常,意味着本层不处理,交给调用它的函数处理
    6.2 处理编译时异常
    ①方法内部try-catch
    ②throws向上抛,如果在main中就别抛了,处理一下,需要注意的是子类重写父类方法的时候,不能抛出比父类更多的编译时异常,最好保持一致
    6.3 throw关键字可以在方法体中抛出一个异常 比如语法格式是throw new RuntimeException("运行时出现了异常"); ,如果抛出的是一个编译时异常,则需要在方法名后添加throws来配合使用
    6.4 异常策略选择:
    ① 对于运行时异常,我们不应该写出产生这种异常的代码,应该在代码的测试阶段修正代码。
    ②对于编译时异常,功能内部能够处理的就处理,如果不能够或者没有必要处理,就抛出。

  2. finally关键字:
    7.1 finally的特点
    被finally控制的语句体一定会执行
    特殊情况:在执行到finally之前jvm退出了(比如System.exit(0))
    7.2 finally的作用
    用于释放资源,在IO流操作和数据库操作中会见到
    7.3 finally的用法:

public static void main(String[] args) {
        //finallyh和try catch一起使用
        try{
            System.out.println("before ");
            System.out.println(10/0);
            System.out.println("after");
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            //一定会执行的代码
            System.out.println("finally执行了");
        }
    }

7.4 一些奇思妙想

  • try代码块如果有return
    • 程序会先执行完finally代码块,回过头执行try中的return
  • catch代码块中如果有return,并且catch正常捕获异常执行
    • 程序会先执行完finally代码块后,再回去执行catch中return,从catch代码块中结束方法
  • finally代码中有return
    • 不会影响finally代码块执行
  • 如果finally和catch中都有return
    • 程序会直接从finally代码块中的return结束方法
  • 如果try中的异常不能正常捕获,但是finally中有return
    • 注意此时程序会跳过这个异常,不会抛出异常给JVM报错
  1. 我们如何去定义一个新的异常
    8.1 如何自定义异常
    编译时异常 : 定义一个类继承 Exception 类
    运行时异常: 定义一个类继承 RuntimeException 类
考试成绩必须在0-100分之间,如果有考试成绩不在这个范围之内,则认为成绩异常。

对于以上的异常,
Java语言中显然没有一个对应的“考试分数异常超出范围”的异常,因此该异常需要我们自己来定义
 */
public class Demo5 {
    public static void main(String[] args) {
        try {
            func();
        } catch (MyException e) {
            e.printStackTrace();
        }
    }

    private static void func() throws MyException{
        // 创建1个Scanner对象
        System.out.println("请输入一个分数:");
        Scanner scanner = new Scanner(System.in);
        // 输入一个分数
        int i = scanner.nextInt();
        // 判断这个分数是否在0-100之间
        if (i < 0 || i > 100) {
            // 如果不在这个范围 抛出异常
            throw new MyException("分数不合法");
        }
    }
}
// 定义一个编译时异常
// 继承Exception
class MyException extends Exception{
    // 2个构造方法

    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}

// 定义一个运行时异常
// 继承RuntimeException
class MyException2 extends RuntimeException{
    // 2个构造方法

    public MyException2() {
    }

    public MyException2(String message) {
        super(message);
    }
}

( 三 ) io

  1. 4个抽象基础类:

①字节输出流 : OutputStream

②字节输入流: InputStream

③字符输出流: Writer

④字符输入流: Reader

由这4个抽象基类派生出的子类都是以其父类名作为后缀的

FileOutputStream

FileInputStream

FileReader

FIleWriter

  1. 处理文本文件(eg: .txt .java),一般都用字节流
  2. try-with-resources
语法:
try(资源,实现了AutoCloseable接口的类都能视作资源){
	出了try代码块之后 ,资源会自动释放(close)
}catch{

}
  1. 输出流
    ①由于继承了Flushable接口,有了flush()方法,可以刷新此输出流并强制写出所有缓冲的输出字节
    ②由于继承了Closeable接口,有close()方法,用以关闭该Stream

  2. 文件字节输出流FileOutputStream

构造方法

FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(File file, boolean append) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
FileOutputStream(String filename) 创建一个向具有指定名称的文件中写入数据的输出文件流。
FileOutputStream(String name, boolean append) 创建一个向具有指定 name 的文件中写入数据的输出文件流。

成员方法

voidwrite(byte[] b) 将 b.length 个字节从指定 byte 数组写入此文件输出流中。
voidwrite(byte[] b, int off, int len) 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。
voidwrite(int b) 将指定字节写入此文件输出流。

注意事项

  • 当我们去创建一个输出流对象的时候,发生了什么?

    • 在创建之前,jvm会向操作系统中找这个文件是否存在
    • 如果不存在, 帮我们创建
    • 如果存在, 会覆盖, 从头开始写
  • 如何实现文件内容的追加写入?

    • 利用带append的构造方法
// 系统默认换行符
        out.write(System.lineSeparator().getBytes());
  1. 文件字节输入流FileInputStream

构造方法

FileInputStream(File file) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
FileInputStream(String filename) 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

成员方法

intread() 从此输入流中读取一个数据字节。
intread(byte[] b) 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。
intread(byte[] b, int off, int len) 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。
  1. 字节缓冲输出流BufferedOutputStream:

构造方法

BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。默认缓冲区大小是8KB
BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流

注意:
1.所有的带缓冲区的输出流都需要flush,除非是缓冲区满了,会自动flush
2.close方法会自动执行flush方法

  1. 字节缓冲输入流:BufferedInputStream:

构造方法

BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。默认缓冲区大小是8KB
BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。

该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统,这是建立下基础已经有的FileInputStream基础上

10.对象流传输
ObjectInputStream与ObjectOutputStream

注意

  • java.io.NotSerializableException 要进行对象流传输需要实现Serializable接口
  • Serializable接口是一个标记接口
  • java.io.InvalidClassException: com.cskaoyan._01othrestream.Student; local class incompatible: stream classdesc serialVersionUID = 9213979889709027197, local class serialVersionUID = 3196328760676831428 怎么结解决 类中显示声明serialVersionUID 必须是static final long
  • 如果类中的某个成员变量 不希望被序列化, tranisent,那么传输了该对象后,该成员变量为默认初始值

成员方法

voidwriteObject(Object obj) 将指定的对象写入 ObjectOutputStream。
ObjectreadObject() 从 ObjectInputStream 读取对象。
  1. 随机访问文件流 RandomAccessFile
    //向文件中插入数据
    @Test
    public void test13() throws IOException {
        File file = new File("a.txt");
        RandomAccessFile rw = new RandomAccessFile(file, "rw");
        //重新移动到插入点
        //创建一个StringBUffer对象
        int readCount;
        rw.seek(3);
        byte[] bytes = new byte[1024];
        StringBuffer sb = new StringBuffer();
        while((readCount = rw.read(bytes))!=-1){
             sb.append(new String(bytes,0,readCount));
        }
        rw.seek(3);
        //插入数据xyz
        rw.write("abcdxyz".getBytes());
        //rw.close();
        //刚刚保存的数据重新写回来
        rw.write(sb.toString().getBytes());

    }

(四)NIO概述 (NOT blocking IO)

NIO核心
  • Buffer
  • Channel

Channel通道表示打开到 IO 设备(例如:文件、 套接字)的连接。若需要使用NIO 系统,需要获取 用于连接 IO 设备的通道以及用于容纳数据的缓冲区Buffer。然后操作缓冲区,对数据进行处理。

一句话概括总结:

Channel负责传输,Buffer负责存储

下面是一个通过Channel和Buffer联动读取数据的代码

    @Test
    public void readTest() throws IOException{
        //通过文件输入字节流获取到一个通道
        FileChannel inChannel = new FileInputStream("a.txt").getChannel();
        //构建缓冲区,大小为16
        ByteBuffer allocate = ByteBuffer.allocate(16);
        //通过通道读取文件内容(写)到缓冲区中
        inChannel.read(allocate);
        allocate.flip();
        //构建第二个缓冲区
        //ByteBuffer allocate1 = ByteBuffer.allocate(8);
        //ByteBuffer[] byteBuffers = {allocate,allocate1};
        //让通道读取数据,从缓冲区中
        //long readCount =  inChannel.read(allocate);
        byte[] bytes = new byte[1024];
        //通过缓冲区的get方法将缓冲区的数据(读)取到bytes中
        allocate.get(bytes,0,allocate.limit());
        //System.out.println(readCount);
        System.out.println(new String(bytes,0,allocate.limit()));
    }

零拷贝的方式,文件复制效率最高

/**
     * 零拷贝复制文件(效率最高)不需要内核用户态转换
     * @throws IOException
     */
    @Test
    public void test21() throws  IOException{
        FileChannel outChannel = new FileOutputStream("a1_copy.txt").getChannel();
        FileChannel inChannel = new FileInputStream("a1.txt").getChannel();
        outChannel.transferFrom(inChannel,0,inChannel.size());

        inChannel.transferTo(0,inChannel.size(),outChannel);

    }

(五)线程

1.多线程的实现方式一:继承Thread类

/*
1. 继承Thread类
2. 重写run方法
3. 创建子类对象
4. 启动线程start()
 */
public class Demo {
    public static void main(String[] args) {
        // 3. 创建子类对象
        MyThread myThread = new MyThread();
        //4. 启动线程start()
        myThread.start();
    }
}
// 1. 继承Thread类
class MyThread extends Thread{
    //2. 重写run方法

    @Override
    public void run() {
        // 放的是要在子线程执行的代码
        for (int i = 0; i < 100; i++) {
            System.out.println(i);
        }
    }
}
  • 执行特点

    • 随机的
  • 一个线程能否多次启动?

    • java.lang.IllegalThreadStateException 不能
  • start方法跟run有啥区别?

    • run只是相当于普通方法调用 并没有开启新的线程 没有新的执行路径
  • 谁才代表一个线程?

    • Thread及其子类对象才代表一个线程
  • 如果run中调用了别的方法 别的方法也运行在子线程中

2.休眠Sleep线程(Thread.sleep(int seconds))模拟

TimeUnit.SECONDS.sleep(1);

public class SleepDemo {
    public static void main(String[] args) {
        SleepThread sleepThread = new SleepThread();
        sleepThread.start();

    }
}

class SleepThread extends Thread{
    @Override
    public void run() {
        for (int i =0;i<10;i++){
            System.out.println(i);
            try{
                TimeUnit.SECONDS.sleep(1);
            }catch (InterruptedException e ){
                e.printStackTrace();
            }
        }
    }
}

3.线程插队:join

voidjoin() 等待该线程终止。

谁等待?

  • main在等待,join这行代码在哪个线程中运行,哪个线程等待

等待谁?

  • 等待的是子线程, 哪个线程调用join, 等待的就是哪个个线程

4.线程礼让yield

static voidyield() 暂停当前正在执行的线程对象,并执行其他线程

创建2个线程 A B 分别打印10个人

A打印0 B打印0 A打印1 B打印1…

结论: 不能做到 随然在这放弃CPU的执行权 但是 还能参与下一轮的CPU的竞争

5.安全的中断线程的例子

通过1个boolean 来作为中断的标记 flag 默认为true

子线程每打印1个数 休眠1s 打印10个数

main线程中每打印1个休眠1s 打印3 个数

main中打印完之后, 立刻中断线程 修改flag值

中断信息保存到日志文件 log.txt "年月日 时分秒 哪个线程发生了中断’

public class SecurityDemo {
    public static void main(String[] args) {
        SeThread st = new SeThread();
        st.setName("李林");
        st.start();
        for (int i = 0;i<3;i++){
            System.out.println(i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //中断线程
        st.flag = false;
    }
}

class SeThread extends Thread{
    boolean flag = true;
    @Override
    public void run(){
        //判断是否发生中断

        for (int i =0;i<10;i++){
            if (flag){
                System.out.println(getName()+"--------"+i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }else {
                //发生了中断
                //将终端信息保存到日志文件log.txt
                //创建字符输出流,日期转换,write
                try {
                    FileWriter fileWriter = new FileWriter("log.txt");
                    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String date = sdf.format(new Date());
                    fileWriter.write(date+getName()+"发生了中断");
                    fileWriter.flush();
                    fileWriter.close();

                } catch (IOException e) {
                    e.printStackTrace();
                }


            }

        }
    }
}

6.线程的生命周期-五种状态

新建

  • 刚new出来的线程 还没start

就绪

  • 执行了start

执行

  • 抢到了CPU的执行权

阻塞

  • 没有CPU的执行权,还缺少一些必要的资源 比如 sleep , join

死亡

  • run方法执行完

7.多线程的实现方式二:实现Runnable接口

步骤

  1. 实现Runnable接口
  2. 重写run方法
  3. 创建子类对象
  4. 创建Thread对象,并且把实现了Runnable的对象作为参数传递
  5. start方法启动

使用匿名内部类生成的方法如下

public class ImpTwo {
    public static void main(String[] args) {
        new Thread(new Runnable(){
            @Override
            public void run(){
                for(int i =0;i<10;i++){
                    System.out.println(Thread.currentThread().getName()+"---------"+i);
                }
            }
        }).start();
        //lambda
        new Thread(()->{
            for(int i =0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+"---------"+i);
            }
        }).start();
    }
注意事项

为什么Runnable中的run方法会运行在子线程中?

class Thread{
    // 定义成员变量
    Runnable target;
    
    init(){
        // 左边是成员变量  右边这个target是传来的参数
        this.target = target;
    }
    
    run(){
        if(target!=null){
            target.run()
        }
    }
}

(六)多线程数据安全问题

1.synchronized

1.1同步代码块

锁对象是谁?哪个对象能够充当锁这个角色?

锁对象可以是任意java对象 但是要保证是同一个

synchronized(锁对象){
    // 对于共享数据的访问操作
}
public class Demo {
    public static void main(String[] args) {
        // 创建线程
        SellWindow sellWindow = new SellWindow();
        Thread t1 = new Thread(sellWindow);
        Thread t2 = new Thread(sellWindow);
        Thread t3 = new Thread(sellWindow);
        t1.setName("A");
        t2.setName("B");
        t3.setName("C");
        // 启动
        t1.start();
        t2.start();
        t3.start();
    }
}

class SellWindow implements Runnable{
// 定义成员变量
    int tickets = 100;
    // 定义一把锁
    // A a = new A();
    Object obj = new Object();
    @Override
    public void run() {
        while (true) {
            // 使用同步代码块
            synchronized (obj) {
                if (tickets > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+
                            "卖了第"+ (tickets--) + "票");
                }
            }

        }
    }
}

1.2 使用同步方法

锁对象是this

public class Demo2 {
    public static void main(String[] args) {
        // 创建线程
        SellWindow2 sellWindow = new SellWindow2();
        Thread t1 = new Thread(sellWindow);
        Thread t2 = new Thread(sellWindow);
        Thread t3 = new Thread(sellWindow);
        t1.setName("A");
        t2.setName("B");
        t3.setName("C");
        // 启动
        t1.start();
        t2.start();
        t3.start();
    }
}

class SellWindow2 implements Runnable {
    // 定义成员变量
    int tickets = 100;
    // 定义一把锁
    // A a = new A();
    int i = 0;

    @Override
    public void run() {
        while (true) {
            if (i % 2 == 0) {
                // 使用同步代码块
                synchronized (this) {
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() +
                                "卖了第" + (tickets--) + "票");
                    }
                }
            } else {
                // 使用同步代码块
                sell();
            }
            i++;


        }
    }

    private synchronized void sell() {

        if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() +
                    "卖了第" + (tickets--) + "票");
        }
    }

}

1.3静态方法

public class Demo2 {
    public static void main(String[] args) {
        // 创建线程
        SellWindow2 sellWindow = new SellWindow2();
        Thread t1 = new Thread(sellWindow);
        Thread t2 = new Thread(sellWindow);
        Thread t3 = new Thread(sellWindow);
        t1.setName("A");
        t2.setName("B");
        t3.setName("C");
        // 启动
        t1.start();
        t2.start();
        t3.start();
    }
}

class SellWindow2 implements Runnable {
    // 定义成员变量
    static int tickets = 100;
    // 定义一把锁
    Object obj = new Object();
    int i = 0;

    @Override
    public void run() {
        while (true) {
            if (i % 2 == 0) {
                // 使用同步代码块
                // 静态方法的锁对象是字节码文件对象
                // Class
                // 对象.getClass()  类名.class  Class.forName("全类名")
                synchronized (SellWindow2.class) {
                    if (tickets > 0) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() +
                                "卖了第" + (tickets--) + "票");
                    }
                }
            } else {
                // 使用同步代码块
                sell();
            }
            i++;


        }
    }

    private static synchronized void sell() {

        if (tickets > 0) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() +
                    "卖了第" + (tickets--) + "票");
        }
    }

}

1.4 细节

执行流程

  • 有A B线程.这2个线程都有访问同步代码块中的内容
  • 假设A抢到了CPU的执行权,先检查锁对象是否可用,A进入同步代码块中.此时发生了线程切换
  • 假设B抢到了CPU的执行权, B也要访问同步代码块中的内容,先检查锁对象是否可用, B线程会处于阻塞状态
  • 又切换回A线程, A线程接着执行, 执行完之后,退出同步代码块, 释放锁对象
  • 下次如果B抢到了,锁对象是可用的, B线程可以访问

如果有异常,也会释放锁对象

2 线程间通信

2.1 wait

1. 阻塞功能:
    当在某线程中,对象上.wait(), 在哪个线程中调用wait(), 导致哪个线程处于阻塞状态
    当某线程,因为调用执行某对象的wait(),而处于阻塞状态,我们说,该线程在该对象上阻塞。
2. 唤醒条件
   当某线程,因为某对象Await(), 而处于阻塞状态时,如果要唤醒该线程,只能在其他线程中,
   再同一个对象(即对象A)上调用其notify()notifyAll()
   即在线程的阻塞对象上,调用notify或notifyAll方法,才能唤醒,在该对象上阻塞的线程
3. 运行条件
      当前线程必须拥有此对象监视器。
      监视器:指synchronized代码块中的锁对象
    即我们只能在,当前线程所持有的synchronized代码块汇中的,锁对象上调用wait方法,
    才能正常执行
    如果我不在同步代码块中调用就会有这样一个异常
    IllegalMonitorStateException
4. 执行特征
      a.该线程发布(release)对此监视器的所有权
      b.等待(阻塞)
   注意:Thread的sleep方法,执行的时候:
        该线程不丢失任何监视器的所属权

练习题:
Thread.sleep VS Object.wait() 有什么区别?

  1. 所属不同:
    a. sleep定义在Thread类,静态方法
    b. wait定义在 Object类中,非静态方法

  2. 唤醒条件不同
    a. sleep: 休眠时间到
    b. wait: 在其他线程中,在同一个锁对象上,调用了notify或notifyAll方法

  3. 使用条件不同:
    a. sleep 没有任何前提条件
    b. wait(), 必须当前线程,持有锁对象,锁对象上调用wait()

  4. 休眠时,对锁对象的持有,不同:(最最核心的区别)
    a. 线程因为sleep方法而处于阻塞状态的时候,在阻塞的时候不会放弃对锁的持有
    b. 但是wait()方法,会在阻塞的时候,放弃锁对象持有

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

赤狐先生

如果有一点点帮助,可以给点支持

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

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

打赏作者

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

抵扣说明:

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

余额充值