多线程(动力)

概述

堆和方法区共享,栈独立

一个线程一个栈

main方法结束只代表主线程结束了,其他线程可能还在执行

思考:

使用了多线程机制之后,main方法结束,是不是有可能程序也不会结束。

main方法结束只是主线程结束了,主栈空了,其他的栈可能还在压栈弹栈

多线程的并发理解

问题: 对于单核的CPU来说,真的可以做到真正的多线程并发吗?


t1线程执行t1的

t2线程执行t2的。t1不影响t2,t2也不影响t1。这叫真正的多线程并发。


单核CPU表示只有一个大脑:

不能做到真正的多线程并发,但是可以做到给人一种”多线程并发“的感觉

对于单核的CPU来说,在某一时间点上实际上只能处理一件事情。只是由于CPU的处理速度极快,在多个线程之间频繁切换执行。


对于多核的CPU电脑说,真正的多线程并发是有的

4核CPU表示同一时间点上,可以真正的有4个进程并发执行

一个栈中,自上而下的顺序依次逐行执行


在Java语言中,堆内存和方法区内存共享

但是栈内存是独立的,一个线程一个栈

多线程的创建

实现线程的第一种方式

继承Thread类(java.lang.Thread)

然后重写run方法

再主方法中创建对象

start()方法的作用:启动一个分支线程,在JVM中开辟一个新的栈空间,在段代码任务完成之后,瞬间就结束了。这段代码的任务只是为了开启一个新的栈空间。只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。

启动成功的线程就会自动调用run方法,并且run方法在分支栈的栈底部(压栈)

run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main方法是平级的

注意:

我们直接调用run方法是不会开启新的线程,不会分配新的栈,单线程,不能并发

我们调用start(),就是开启一个新的线程,会分配一个新的栈

下面是用run方法调用:

image-20211122104554998

使用start()方法调用多线程

image-20211122111959092

第二种方式Runnable接口

实现java.lang.Runnable接口,实现run方法


使用匿名内部类创建线程对象

public class ThreadTest04{
    //采用匿名内部类创建线程
    Thread t = new Thread(new Runnable(){
        @Override
        public void run(){
            //业务代码
        }
    });
    //启动线程
    t.start();
}
}

两种方式对比

建议我们用第二种方式,因为第一种方式有局限性且第二种方式是面向接口编程。

Java是单继承多实现。所以使用第二种方式更加灵活。

多线程的生命周期图解

image-20211122125454180

新建状态

就绪状态

运行状态

阻塞状态

死亡状态


![image-20211117161753868](F:\记录\PicGo\Java Web\image-20211117161753868.png)

在官方文档里,是有6种线程状态的

1、new:尚未开启线程处于此状态

2、Runnable :在Java虚拟机中执行的线程处于此状态(注意runnable状态不是正在运行状态 ,只是表示可以运行了,具体运行还需要看线程调度器来控制)

3、Blocked:被阻塞等待监视器锁定的线程处于此状态

4、waiting:正在等待另一个线程执行特定动作的线程处于此状态

5、Time_waiting:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态(超时等待

6、Terminated:已退出的线程处于此状态

多线程的常用函数方法

获取线程的名字

设置线程的名字:xx.setName("xxxx")

获取线 程的名字:x.getName()

public class ThreadTest{
    public static void main(String[] args){
        //创建线程对象
        Thread t1 = new MyThread();
        //设置名字
        t1.setName("One");
        System.out.println(t1.getName());
        t1.start();
    }
}

class MyThread extends Thread{
    public void run () {
        for(int i = 0 ; i <100; i++){
            System.out.println("分支:"+i);
        }
    }
}

获取当前线程对象

java.lang.Thread类中

有个static Thread方法,其中方法名是currentThread()

那么怎么获取当前线程对象?

Thread t = Thread.currentThread()

currentThread就是当前线程对象

Thread.currentThread()方法写在哪个线程里就是获取当前线程的对象。 如在main方法中就是获取main方法对象,写在其他分线程中就是获取分线程的对象

线程的Sleep方法

static void sleep(long millis)
1、静态方法:Thread.sleep(1000)
2、参数是毫秒
3、作用:让当前线程进入休眠,进入“阻塞”状态,放弃占有cpu时间片,让给其他线程使用

public class SleepMethod {

    /*
     * 关于线程的sleep方法
     * static void sleep(long millis)
     public class SleepMethod {

    /*
     * 关于线程的sleep方法
     * static void sleep(long millis)
     * 1、静态方法:Thread.sleep(1000)
     * 2、参数是毫秒
     * 3、作用:让当前线程进入休眠,进入“阻塞”状态,放弃占有cpu时间片,让给其他线程使用
     * 4、Thread.sleep():该方法可以做到:每隔特定的时间,去执行一段特定的代码。
     * */
    public static void main(String[] args) {
        System.out.println("Hello World!");
        try {
            //休眠2秒
            Thread.sleep(1000 * 2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Hello World!");

        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "------>" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}java
     * 4、Thread.sleep():该方法可以做到:每隔特定的时间,去执行一段特定的代码。
     * */
    public static void main(String[] args) {
        System.out.println("Hello World!");
        try {
            //休眠2秒
            Thread.sleep(1000 * 2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Hello World!");

        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "------>" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

关于sleep的面试题

public class SleepInterview {
    public static void main(String[] args) {
        Thread t1 = new MyThread();
        t1.setName("T");
        t1.start();

        try {
            //问题:这个代码会让线程t1进入休眠状态吗?
            t1.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Hello!");
    }
}

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "----->" + i);
        }
    }
}

不会,因为t1.start()方法在执行的时候还是会转换成:Thread.sleep()这行代码的作用是:让当前线程进入休眠状态 ,也就是让main线程进入休眠

终止线程的睡眠方法

终止线程的具体方法名:

x.interrupt()

public class EndSleepMethod {
    /*
     * 终止休眠的方法
     * 1、sleep休眠太久了,如果希望半道上醒来,我们就应该唤醒一个正在睡眠的线程
     * 这个不是终断线程的执行,是终断线程的睡眠。
     * */
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable2());
        t.setName("ttt");
        t.start();

        //希望5秒后,t线程醒来
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //终断t线程的睡眠(这种终断睡眠的方式是依靠了Java异常的处理机制)
        t.interrupt();
    }
}

class MyRunnable2 implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "---->begin");
        try {
            Thread.sleep(1000 * 60 * 60);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "--->end");
    }
}

强行终止线程的方法(stop) --不推荐使用

stop()方法的是存在缺点的,是因为容易丢失数据。

因为这种方式是直接将线程杀死了,线程如果还没有保存数据,那么就会丢失数据。所以不建议使用

public class EndSleepMethod1 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable3());
        t.setName("分线程");
        t.start();

        //模拟5秒
        try {
            Thread.sleep(1000 * 5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //5秒之后强行终止t线程
        t.stop(); //已过时(不建议使用)
    }
}

class MyRunnable3 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "---->" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

合理强行终止线程的方法()--推荐使用

使用布尔值打一个标记

public class EndSleepMethod2 {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread t = new Thread(myRunnable);
        t.setName("T");
        t.start();
        //模拟5s
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //终止线程
        //我们想要什么时候终止t的执行,那么把标记改为false就OK了
        myRunnable.run = false;
    }
}

class MyRunnable implements Runnable {
    //打一个布尔标记
    boolean run = true;

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (run) {
                System.out.println(Thread.currentThread().getName() + "--->" + i);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            } else {
                //终止当前线程
                return;
            }
        }
    }
}

线程的调度(了解)

概述

常见的线程调度模型有什么?

抢占式调度模型

​ 哪个线程的优先级比较高,那么抢到的cpu时间片的概率就高一些

Java采用的就是抢占式调度模型。


均分布调度模式:

​ 平均分配Cpu时间片,每个线程占有的cpu时间片时间长度一样。平均分配,一切平等

线程调度的方法

实例方法:

void setPriority (int newPriority)设置线程的优先级

int getPriority()获取线程的优先级

注意

  • 最低优先级是1 MIN_PRIORITY
  • 默认优先级是5 NORM_PRIORITY
  • 最高优先级是10 MAX_PRIORITY

优先级比较高点的获取的CPU时间片的时间可能会多一点

静发方法

static void yield() 让位方法

表示暂停当前正在执行的线程对象,并执行其他线程

注意

1、yield()方法不是阻塞方法,只是表示当前线程让位,先给其他线程使用。

2、yield()的执行会让当前线程从“运行状态”回到“就绪状态”。

3、如果回到就绪状态之后,也还可能再次抢到时间片

public class YieldMethod {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyT());
        t1.setName("1111");
        t1.start();

        for (int i = 1; i < 10000; i++) {
            System.out.println(Thread.currentThread().getName()+ "----->" + i);
        }
    }
}
class MyT implements Runnable{
    @Override
    public void run() {
        for (int i = 1; i < 10000; i++) {
            //每100个让位一次
            if (i %100 == 0){
                Thread.yield(); //当前线程暂停一下,让给主线程
            }
            System.out.println(Thread.currentThread().getName()+ "----->" + i);
        }
    }
}

实例方法

void join() 合并线程

class M1 extends Thread{
    public void run(){
        M2 t = new M2();
        t.join();  //当前线程进入阻塞,t线程执行,直到t线程结束,当前线程才执行
    }
}
class M2 extends Thread{
    
}
public class JoinMethod {
    public static void main(String[] args) {
        Thread t = new Thread(new T());
        t.setName("t");
        t.start();

        try {
            t.join();  //当前线程阻塞,t线程执行直到结束
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main Over");
    }
}

class T implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

线程的安全问题(重点)

因为以后我们的项目都是在服务器中,服务器已经将线程的定义与线程对象的创建,线程的启动等都已经实现完了。

我们就需要关注这些数据在多线程并发的环境下是否安全。


什么时候数据在多线程并发的环境下存在安全问题

什么时候数据在多线程并发的环境下会存在安全问题?

  • 多线程并发
  • 有共享数据
  • 共享数据有修改的行为

满足上诉三个条件就可能存在安全问题。

解决线程安全问题

线程排队执行即可

也称为线程同步机制

专业术语:线程同步 实际就行线程不能并发了,必须排队执行

线程同步和异步的理解

异步编程模型:

线程a和线程b,各自执行各自的,a不管b,b不管a,谁也不需要等谁

多线程并发(效率较高)

异步就是并发

同步编程模型:

线程a和线程b,在线程a执行的时候,必须等待b线程执行结束,或者说b线程执行的时候,必须等待a线程执行结束

两个线程之间发生了等待关系,这就是同步编程模型。

效率低。

同步就是排队

具体实现Synchronized() - 排他锁

Account

public class Account {
    //账户
    private String actno;
    //余额
    private double balance;

    public Account() {
    }
    public Account(String actno, double balance) {
        this.actno = actno;
        this.balance = balance;
    }
    public String getActno() {
        return actno;
    }
    public void setActno(String actno) {
        this.actno = actno;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }

    //取款方法
    public void withdraw(double money) {
        //取款之前的余额
        double balance = this.balance;
        //取款之后
        double after = balance - money;
        //更新余额
        this.setBalance(after);
    }
}

AccountThread

public class AccountThread extends Thread {
    //两个线程必须共享一个账户对象
    private Account act;

    //通过构造方法传递过来账户对象
    public AccountThread(Account act) {
        this.act = act;
    }

    @Override
    public void run() {
        synchronized (this) {
            //run方法的执行表示取款操作
            //取款5k
            double money = 5000;
            act.withdraw(money);
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("成功:" + "余额还有" + act.getBalance());
        }
    }
}

public class Test {
    public static void main(String[] args) {
        Account act = new Account("001", 10000);
        Thread t1= new AccountThread(act);
        Thread t2= new AccountThread(act);
        t1.setName("1");
        t2.setName("2");
        t1.start();
        t2.start();
    }
}

注意:

synchronized(){
//( )里面的对象是共享对象,通常是this,也可以是其他的 。只要是同一个对象就可以了
}

也可使用同步方法:

这种方式共享对象只能是this了,还有有个缺点就是会无故扩大同步的范围,导致程序的执行效率降低。

优点:代码写的少了。

如果我们共享的对象就是this,并且需要同步的代码块是整个方法体,那么就建议使用这个方法。

public class Account {
    //账户
    private String actno;
    //余额
    private double balance;

    public Account() {
    }
    public Account(String actno, double balance) {
        this.actno = actno;
        this.balance = balance;
    }
    public String getActno() {
        return actno;
    }
    public void setActno(String actno) {
        this.actno = actno;
    }
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }

    //取款方法
    public synchronized void withdraw(double money) {
        //取款之前的余额
        double balance = this.balance;
        //取款之后
        double after = balance - money;
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //更新余额
        this.setBalance(after);
    }
}

Java的三大变量(重要知识点)

  • 实例变量:在堆中
  • 静态变量:在方法区
  • 局部变量:在栈中

以上三大变量中,局部变量永远不会存在线程安全问题。

因为局部变量不共享---(因为一个线程一个栈


静态变量在方法区中,方法区只有1个。

堆和方法区都是多线程共享的,所以可能存在线程安全问题。

具体实现续集

如果使用局部变量的话,建议使用StringBuilder

因为局部变量不存在线程安全问题

如果选择StringBuffer效率比较低

ArrayList是非线程安全的

Vector是线程安全的

HashMap HashSet是非线程安全的

HashTable是线程安全的

总结

Synchronize有三种方法:

  • 同步代码块

synchronized(线程共享对象){

同步代码块

}

第二种方法:

  • 在实例方法上使用synchronized

表示共享对象一定是this,并且同步代码块是整个方法体

三种:

  • 在静态方法上使用synchronized (保证静态变量的安全)

表示类锁

类锁永远只有1个

对象锁:1个对象1把锁,100个对象100把锁

类锁:100个对象,也可能只是1把锁

面试题:

//面试题:doOther方法执行的时候需要等待doSome方法的结束吗?
/*
 * 不需要,因为下面的doOther方法没有synchronized方法
 * */
public class Exam01 {
    public static void main(String[] args) {
        MyClass mc = new MyClass();
        Thread t1 = new MyThread(mc);
        Thread t2 = new MyThread(mc);
        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        try {
            Thread.sleep(1000);  //作用:保证t1线程先执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}
class MyThread extends Thread {
    private MyClass mc;
    public MyThread(MyClass mc) {
        this.mc = mc;
    }
    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("t1")) {
            mc.doSome();
        }
        if (Thread.currentThread().getName().equals("t2")) {
            mc.doOther();
        }
    }
}
class MyClass {
    public synchronized void doSome() {
        System.out.println("begin");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Over");
    }
    public void doOther() {
        System.out.println("Other begin");
        System.out.println("Other Over");
    }
}

面试二:

//面试题:doOther方法执行的时候需要等待doSome方法的结束吗?
//需要,因为doOther方法也有synchronized来同步,而doSome和doOther都是用同步方法,而锁对象这是本类的this
public class Exam02 {
    public static void main(String[] args) {
        MyClass mc = new MyClass();
        Thread t1 = new MyThread(mc);
        Thread t2 = new MyThread(mc);
        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        try {
            Thread.sleep(1000);  //作用:保证t1线程先执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}

class MyThread extends Thread {
    private MyClass mc;

    public MyThread(MyClass mc) {
        this.mc = mc;
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("t1")) {
            mc.doSome();
        }
        if (Thread.currentThread().getName().equals("t2")) {
            mc.doOther();
        }
    }
}

class MyClass {
    public synchronized void doSome() {
        System.out.println("begin");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Over");
    }

    public synchronized void doOther() {
        System.out.println("Other begin");
        System.out.println("Other Over");
    }
}

面试三:

//面试题:doOther方法执行的时候需要等待doSome方法的结束吗?
//需要,因为静态方法是类锁,不管创建了几个对象,类锁只有一把
public class Exam02 {
    public static void main(String[] args) {
        MyClass mc = new MyClass();
        Thread t1 = new MyThread(mc);
        Thread t2 = new MyThread(mc);
        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        try {
            Thread.sleep(1000);  //作用:保证t1线程先执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t2.start();
    }
}

class MyThread extends Thread {
    private MyClass mc;

    public MyThread(MyClass mc) {
        this.mc = mc;
    }

    @Override
    public void run() {
        if (Thread.currentThread().getName().equals("t1")) {
            mc.doSome();
        }
        if (Thread.currentThread().getName().equals("t2")) {
            mc.doOther();
        }
    }
}

class MyClass {
    public synchronized static void doSome() {
        System.out.println("begin");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Over");
    }

    public synchronized static void doOther() {
        System.out.println("Other begin");
        System.out.println("Other Over");
    }
}

开发中应该怎么解决线程安全问题

​ 一上来就选择线程同步吗?synchronized()

不是,synchronized会让程序的执行效率降低,用户体验不好。并且要降低用户的吞吐量,用户体验极差。所以在不得已的情况下再选择线程同步机制。

解决方法:

第一种方法:尽量使用局部变量代替实例变量和静态变量。

第二种方法:如果必须是实例变量,那么可以考虑创建多个对象,这样实例变量的内存就不共享了。(一个线程对应一个对象,100个线程对应100个对象)

第三种方法:如果不能使用局部变量,对象也不能创建多个,这个时候也只能选择synchronized,线程同步机制

死锁的概述

比如线程a 和线程b
线程a要先锁住o1 再锁o2
线程b要先锁o2,再锁o1
就会出现死锁。
因为a锁o2的时候被b锁住了
b锁o1的时候被a锁住了 

死锁:不出现异常,也不会出现错误,程序一直停在那里。

死锁的代码演示

public class DeadLock {
    public static void main(String[] args) {
        Object o1 = new Object();
        Object o2 = new Object();
        //t1和t2两个线程共享o1,o2
        Thread t1 = new MyThread(o1, o2);
        Thread t2 = new MyThread2(o1, o2);
    }
}

class MyThread extends Thread {
    Object o1;
    Object o2;
    public MyThread(Object o1, Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }
    @Override
    public void run() {
        synchronized (o1) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o2) {
            }
        }
    }
}

class MyThread2 extends Thread {
    Object o1;
    Object o2;
    public MyThread2(Object o1, Object o2) {
        this.o1 = o1;
        this.o2 = o2;
    }
    @Override
    public void run() {
        synchronized (o2) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o1) {
            }
        }
    }
}

守护线程

守护线程可以理解为垃圾回收器

Java语言中有两个线程:

  • 用户线程:主线程等等
  • 守护线程:也称后台线程。

其中守护线程具有代表性的就是:垃圾回收线程(守护线程)

守护线程的特点

一般守护线程是一个死循环,所有的用户线程只要结束,守护线程也将自动结束。

注意:主线程main方法是一个用户线程

模拟一个守护线程

xx.setDaemon(true)

public class DaemonThread {
    public static void main(String[] args) {
        Thread b = new B();
        b.setName("备份线程");
        b.setDaemon(true);
        b.start();

        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}

class B extends Thread {
    @Override
    public void run() {
        int i = 0;
        while (true) {
            System.out.println(Thread.currentThread().getName() + "--->" + (++i));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

定时器

间隔特定的时间,执行特定的程序。

在实际的开发中,每隔一段特定的时间执行一段特定的程序,这种需求是很常见的,那么在Java中其实可以采用多种方式实现:

可以使用sleep方法,睡眠,设置睡眠时间,每到这个时间点醒来,执行任务 (原始方法)

在Java的类库中已经写好了一个定时器:Java.util.Timer,可以直接拿来用。但是这种方法在目前的开发中也很少用,因为现在很多高级框架都是支持定时任务的

实现线程的第三种方式

FutureTask方式,实现callable接口(JDK1.8新特性)

这种方式实现的线程可以获取线程的返回值

之前的两种方式不能获取线程的返回值

image-20211201195920740

wait和notify方法

关于Object类中的wait和notify方法(生产者和消费者模式!)

第一:wait和notify方法不是线程对象的方法,是java中任何一个java对象都有的方法,因为这两个方式是Object类自带的

wait方法和notify方法不是通过线程对象调用

如:t.wait(),t.notify()。。是不对的


第二:wait()方法作用?

Object o = new Object();
o.wait();

表示让正在o对象上活动的线程进入等待状态,无期限等待,直到被唤醒为止。

直到最终调用o.notify()方法才能被唤醒

notify()方法可以让正在wait的线程醒来。


第三:notify()方法的作用?

Object o = new Object();

o.notify();

表示:

唤醒正在o对象上等待的线程。

还有一个notifyAll( )方法:

​ 这个方法是唤醒o对象上处于等待的所有线程。

wait方法和notify方法建立在synchronized线程同步的基础之上

o.wait0方法会让正在o对象上活动的当前线程进入等待状态,并且释放之前占有的o对象的锁

o.notify()方法只会通知,不会释放之前占有的o对象的锁。

posted @ 2021-12-05 16:27  喂s别闹  阅读(10)  评论(0编辑  收藏  举报