Java多线程基础

线程相关概念

程序:是为完成特定任务,用某种语言编写的一组指令的集合,简单来说:就是我们写的代码

进程:

  1. 是指运行中的程序,比如我们使用QQ,就启动了一个进程,操作系统就会为该进程分配内存空间,而当我们使用别的软件,就又启动了一个进程,操作系统就会为其分配新的内存空间
  2. 进程是程序的一次执行过程,或是正在运行的一个程序。是动态过程,又它自身的产生、存在和消亡的过程

线程

  1. 线程由进程创建的,是进程的一个实体
  2. 一个进程可以拥有多个线程

其他相关概念

  1. **单线程:**同一个时刻,只允许执行一个线程
  2. 多线程:同一个时刻,可以执行多个线程,比如:一个qq进程,可以打开多个聊天窗口;一个迅雷进程,可以同时下载多个文件
  3. **并发:同一个时刻,多个任务交替执行,造成一种“貌似同时”的错觉,简单来说,**单核cpu实现的多任务就是并发
  4. **并行:同一个时刻,多个任务同时执行。**多核cpu可以实现并行
  • 并发和并行可以同时有:假设电脑只有2个CPU,开了3个程序,那么2个CPU,只能同时执行2个任务,而另1个任务只能并发

线程的基本使用

创建线程的两种方式

在Java中线程使用有两种方式

  1. 继承Thread类,重写run方法
  2. 实现Runnable接口,重写run方法

img

线程应用案例1-继承Tread类

  1. 编写程序,开启一个线程,该线程每隔1秒,在控制台输出“喵喵,我是小猫咪”
  2. 当输出80次喵喵,我是小猫咪后,结束该线程
  3. 使用JConsole监控线程执行情况
package com.zanedu.threaduse;

//通过继承 Thread 类创建线程
public class Thread01 {
    public static void main(String[] args) {
        //创建Cat对象,可以当作线程使用
        Cat cat = new Cat();
        //启动线程 -> 最终会执行 Cat类的run方法

        //解读源码
        /*
        1.  public synchronized void start() {
                start0();
            }
        2.  start0() 是native方法,是本地方法,由JVM调用,底层是c/c++实现
            真正实现多线程的效果,是 start0(),而不是run方法
            private native void start0();
         */

        cat.start();//只有start会启动线程



//        cat.run();//run方法就是一个普通的方法,并没有真正的启动线程,这样就会把run方法执行完毕,才会继续向下执行,即堵塞
        //说明:当main线程启动一个子线程 Thread-0,主线程不会堵塞,即主线程会继续执行
        //这时 主线程和子线程是交替执行的
        System.out.println("===主线程继续执行===" + Thread.currentThread().getName());//main
        for (int i = 0; i < 60; i++) {
            System.out.println("主线程 i=" + i);
            //让主线程休眠
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//说明
//1. 当一个类继承了 Thread类,该类就可以当作线程使用
//2. 我们会重写 run方法,写上自己的业务代码
//3. run方法是 Thread类实现了 Runnable接口的run方法
/*
        @Override
        public void run() {
            if (target != null) {
                target.run();
            }
        }
 */
class Cat extends Thread {

    int time = 0;

    @Override
    public void run() { //重写run方法,写上自己的业务逻辑
        while (true) {
            //该线程每隔1秒,在控制台输出“喵喵,我是小猫咪”
            System.out.println("喵喵,我是小猫咪" + (++time) + " 线程名=" + Thread.currentThread().getName());
            //让该线程休眠一秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (time == 80) {
                break;//当time到80,就退出while循环,这时线程也就退出了
            }
        }
    }
}
  • 解释说明:为什么要用start(),而不是直接调用run方法

run方法就是一个普通的方法,并没有真正的启动线程,这样就会把run方法执行完毕,才会继续向下执行,即堵塞main方法

而start会启动一个子线程 Thread-0,并且主线程不会堵塞,会继续执行下去

  • 看一下start的源码

重要源码:

public synchronized void start() {

start0();

}

img

  • 进入到start0()内部后,会去跑run方法
  • start0() 是native方法,是本地方法,由JVM调用,底层是c/c++实现
  • 因此真正实现多线程的效果,是 start0(),而不是run方法

img

  • 看一下主线程和子线程的关系

img

  • 我们可以看到,去跑代码的时候,它首先开了一个进程,然后启动了main主线程,然后又开启了子线程,当60次后主线程挂了,但是子线程还没有结束,因此还在工作,所以进程还活着,而当子线程输完80个的时候,它就挂了,即整个线程就挂了

总结:在多线程中,不是主线程挂了之后,整个线程就挂了,而是不一定,可能子线程还没有挂,因此只有所有的线程结束了,进程才结束

  • JConsole一下上面代码:
  • 主线程还没挂

img

  • 主线程挂了

img

img

  • start()方法调用start0()方法后,该线程并不一定立马执行,只是将线程变成了可运行状态,具体什么时候执行,取决于CPU,由CPU统一调度

线程应用案例2-实现Runnable接口

  • 说明:
  1. Java是单继承的**,在某些情况下可能已经继承了某个父类,**这时在用继承Thread类的方法来创建线程显然不可能了
  2. 因此Java设计者们提供了另外一个方式来创建线程,就是通过****实现Runnable接口来创建线程

**应用案例:编写程序,该程序可以每隔1秒,在控制台输出"hi",当输出10次后,自动退出,使用实现Runnable接口的方式来实现,**这里底层使用了设计模式[代理模式]

package com.zanedu.threaduse;

//通过实现接口Runnable 来开发线程
public class Thread02 {
    public static void main(String[] args) {
        Dog dog = new Dog();
//        dog.start(); //这里不能调用start
        //创建了Thread对象,把dog对象(实现Runnable),放入Thread
        Thread thread = new Thread(dog);
        thread.start();
    }
}

class Dog implements Runnable { //通过实现Runnable接口,开发线程

    int count = 0;
    @Override
    public void run() { //普通方法
        while (true) {
            System.out.println("小狗,汪汪叫..hi" + (++count) + Thread.currentThread().getName());

            //休眠一秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 10) {
                break;
            }
        }
    }
}
  • 设计模式[代理模式]
package com.zanedu.threaduse;

//通过实现接口Runnable 来开发线程
public class Thread02 {
    public static void main(String[] args) {
        //测试代理
       Tiger tiger = new Tiger(); //实现了Runnable接口
       ThreadProxy threadProxy = new ThreadProxy(tiger);
       threadProxy.start();
    }
}
//静态代理模式
class Animal {}
class Tiger extends Animal implements Runnable{

    @Override
    public void run() {
        System.out.println("老虎,嗷嗷叫...");
    }
}

//线程代理类,模拟了一个极简的 Thread
class ThreadProxy implements Runnable{ //可以把 ThreadProxy类当作 Thread类

    private Runnable target = null;//属性,类型是Runnable

    @Override
    public void run() {
        if (target != null) {
            target.run();//动态绑定 (运行类型Tiger)
        }
    }

    public ThreadProxy(Runnable target) {
        this.target = target;
    }

    public void start() {
        start0();//这个方法是真正实现多线程的方法
    }
    public void start0() {
        run();
    }
}

线程使用应用案例-多线程执行

编写程序,创建两个线程,一个线程每隔1秒输出"hello,world",输出10次退出,一个线程每隔1秒输出"hi",输出5次退出

package com.zanedu.threaduse;

//在main线程启动2个子线程
public class Thread03 {
    public static void main(String[] args) {
        T1 t1 = new T1();
        T2 t2 = new T2();
        Thread thread = new Thread(t1);
        Thread thread1 = new Thread(t2);
        thread.start();//启动第一个线程
        thread1.start();//启动第二个线程
    }
}

class T1 implements Runnable {

    int count = 0;
    @Override
    public void run() {
        //每隔1秒输出“hello,world”,输出10次
        while (true) {
            System.out.println("hello, world" + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 10) {
                break;
            }
        }
    }
}

class T2 implements Runnable {

    int count = 0;
    @Override
    public void run() {
        while (true) {
            //每隔1秒输出“hi”,输出5次
            System.out.println("hi" + (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 5) {
                break;
            }
        }
    }
}

线程的理解

img

img

继承Thread vs 实现Runnable的区别

  1. 从Java的设计来看,通过继承Thread或者实现Runnable接口来创建线程本质上没有区别,毕竟Thread类本身就实现了Runnable接口
  2. 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制,建议使用Runnable

即实现了Runnable接口的对象,可以将其传入多个线程,即可以传入这个线程,也可以传入另一个线程

  • 【售票系统】,编程模拟三个售票窗口,总共售票100张,分析会有什么问题
package com.zanedu.ticket;

//使用多线程模拟三个窗口同时售票,售票100张
public class SellTicket {
    public static void main(String[] args) {
//        //测试Thread
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
//
//        //这里我们会出现超卖现象
//        sellTicket01.start();//启动售票线程
//        sellTicket02.start();//启动售票线程
//        sellTicket03.start();//启动售票线程

        //测试Runnable
        System.out.println("===使用实现接口的方式来售票===");
        SellTicket02 sellTicket02 = new SellTicket02();
        new Thread(sellTicket02).start();//第1个线程 - 窗口
        new Thread(sellTicket02).start();//第1个线程 - 窗口
        new Thread(sellTicket02).start();//第1个线程 - 窗口
    }
}

//使用Thread方式
class SellTicket01 extends Thread {

    private static int ticketNum = 100;//让多个线程共享 ticketNum

    @Override
    public void run() {
        while (true) {
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                break;
            }
            //休眠50毫秒,模拟人休息
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" +
                    " 剩余票数=" + (--ticketNum));
        }
    }
}

//实现接口方式
class SellTicket02 implements Runnable {
    private int ticketNum = 100;//让多个线程共享 ticketNum

    @Override
    public void run() {
        while (true) {
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                break;
            }
            //休眠50毫秒,模拟人休息
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" +
                    " 剩余票数=" + (--ticketNum));
        }
    }
}
  • 出现以下这种情况是因为突然间3个窗口都进入到这个线程里,而即当只有1张票时,一边的线程还没有卖完,另一边的线程就进来了,导致出现超卖现象

img

  • 这时我们就需要使用线程终止的方式来结束线程

线程终止

基本说明

  1. 当线程完成任务后,会自动退出
  2. 还可以通过使用变量来控制run方法退出的方式停止线程,即****通知方式

应用案例

  • 需求:启动一个线程t,要求在main线程中去停止线程t
package com.zanedu.exit_;

public class ThreadExit_ {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();

        //希望main线程去控制 t线程的终止,就必须修改loop即可
        //让 t 退出run方法,从而终止t线程 -> 通知方式
        //让主线程休眠10秒,再通知t线程退出
        System.out.println("===主线程休眠10秒===");
        Thread.sleep(10 * 1000);
        t.setLoop(false);
    }
}

class T extends Thread{
    private int count = 0;
    //设置一个控制变量
    private boolean loop = true;
    @Override
    public void run() {
        while (loop) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("T 运行中..." + (++count));
        }
    }

    public void setLoop(boolean loop) {
        this.loop = loop;
    }
}

线程常用方法

常用方法第一组

  1. setName:设置线程名称,使之与参数name相同
  2. getName:返回该线程的名称
  3. **start:使该线程开始执行,**Java虚拟机(JVM)底层调用该线程的start0()方法
  4. run:调用线程对象run方法
  5. setPriority:更改线程的优先级
  6. getPriority:获取线程的优先级
  7. sleep:在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
  8. **interrupt:**中断线程,不是停止线程,只是抛出一个异常出来
package com.zanedu.method;

public class ThreadMethod01 {
    public static void main(String[] args) throws InterruptedException{
        //测试相关方法
        T t = new T();
        t.setName("zan");
        t.setPriority(Thread.MIN_PRIORITY);//设置线程的优先级
        t.start();//启动子线程

        //主线程输出打印 5句hi,就中断子线程的休眠
        for (int i = 0; i < 5; i++) {
            Thread.sleep(1000);
            System.out.println("hi" + i);
        }
        System.out.println(t.getName() + " 线程的优先级 = " + t.getPriority());
        t.interrupt();//当执行到这里,就会中断 t线程的休眠
    }
}

class T extends Thread { //自定义的线程类
    @Override
    public void run() {
        while (true) {
            for (int i = 0; i < 100; i++) {
                //Thread.currentThread().getName() 获取当前线程的名称
                System.out.println(Thread.currentThread().getName() + " 吃包子~~~~");
            }
            try {
                System.out.println(Thread.currentThread().getName() + " 休眠中~~~~");
                Thread.sleep(20000);//20秒
            } catch (InterruptedException e) {
                //当该线程执行到一个interrupt方法时,就会catch一个异常,可以加入自己的业务代码
                //InterruptedException 是捕获到到一个中断异常
                System.out.println(Thread.currentThread().getName() + "被 interrupt了");
            }
        }

    }
}
  • 使用interrupt,中断线程,即在这块是使休眠被中断,即线程继续

img

注意事项和细节

  1. **start底层会创建新的线程,调用run,**而run方法就是一个简单的方法调用,不会启动新线程
  2. 线程优先级的范围 - 3类

img

  1. interrupt:中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程
  2. sleep:线程的静态方法,使当前线程休眠

常用方法第二组

  1. yield:线程的礼让,礼让cpu,让其他线程执行,但礼让的时间不确定,所以****不一定能礼让成功
  2. **join:线程的插队。插队的线程一旦插队成功,**则肯定先执行完插入的线程所有的任务,才会继续执行别的任务
package com.zanedu.method;

public class ThreadMethod02 {
    public static void main(String[] args) throws InterruptedException{
        T2 t2 = new T2();
        t2.start();
        for (int i = 1; i <= 20; i++) {
            Thread.sleep(1000);
            System.out.println("主线程 吃了 " + i + " 个包子");
            if (i == 5) {
                System.out.println("主线程让子线程先吃");
                //join,线程插队
                //t2.join();//这里相当于让子线程先执行完毕

                Thread.yield();//礼让,不一定成功

                System.out.println("子线程吃完了,主线程再接着吃");
            }
        }
    }
}

class T2 extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 20; i++) {
            try {
                Thread.sleep(1000);//休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("子线程 吃了 " + i + " 个包子");
        }
    }
}
  • yield没有礼让成功

img

  • join插队,先完成子线程,再跑主线程

img

img

用户线程和守护线程

  1. 用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
  2. 守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
  3. 常见的守护线程:垃圾回收机制
package com.zanedu.method;

public class ThreadMethod03 {
    public static void main(String[] args) throws InterruptedException {
        MyDaemonThread myDaemonThread = new MyDaemonThread();
        //如果我们希望当main主线程结束后,子线程可以自动结束
        //只需要将子线程设置为守护线程即可
        //注意:要先设置,再启动子线程
        myDaemonThread.setDaemon(true);
        myDaemonThread.start();

        for (int i = 1; i <= 10; i++) { //main线程
            System.out.println("宝强在辛苦的工作...");
            Thread.sleep(1000);
        }

    }
}

class MyDaemonThread extends Thread {
    @Override
    public void run() {
        for(; ; ) { //无限循环
            try {
                Thread.sleep(1000);//休眠1秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("马蓉和宋喆快乐聊天,哈哈哈~~~");
        }
    }
}

线程的生命周期

JDK中用Thread.State枚举表示了线程的几种状态

img

  • 从官网文档看线程有6种状态,而更细化的话,可以将Runnable状态划分为Ready状态和Running状态,即7种状态

线程状态转换图(重点-涉及操作系统)

img

线程的同步-synchronized

线程同步机制

  1. 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何同一时刻,最多有一个线程访问,以保证数据的完整性
  2. **也可以这样理解:线程同步,**即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作

同步具体方法-synchronized

  1. 同步代码块
synchronized (对象) { //得到对象的锁,才能操作同步代码
    //需要被同步代码;
}
  1. synchronized还可以放在方法声明中,表示整个方法为同步方法
public synchronized void m(String name) {
    //需要被同步的代码
}
  • 理解:好比某小伙伴上厕所先把门关上(上锁),完事后再出来(解锁),那么其他小伙伴就可以使用厕所了

分析同步原理

img

  • 比如t1抢到了锁,然后将锁关闭,只有当t1出来的时候,t2、t3才有可能抢到锁进去,但是同时t1也可以再次抢锁,依次类推

互斥锁

基本介绍

  1. Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性
  2. 每个对象都对应于一个可称为"互斥锁"的标记,整个标记用来保证在任一时刻,只能有一个线程访问该对象
  3. 关键字synchronized来与对象的互斥锁联系。当某个对象要用synchronized修饰时,表明该对象在任一时刻只能由一个线程访问
  4. 同步的局限性:导致程序的执行效率要降低【由于会堵塞,只有抢到锁才能出去】
  5. 同步方法(非静态的)的锁可以是this,也可以是其他对象(但要求是同一对象)
  6. 同步方法(静态的)的锁为当前类本身

使用互斥锁解决售票问题

package com.zanedu.syn;

//使用多线程模拟三个窗口同时售票,售票100张
public class SellTicket {
    public static void main(String[] args) {
        //测试一把
        System.out.println("===使用synchronized实现线程同步来售票===");
        SellTicket03 sellTicket03 = new SellTicket03();
        new Thread(sellTicket03).start();//第1个线程 - 窗口
        new Thread(sellTicket03).start();//第1个线程 - 窗口
        new Thread(sellTicket03).start();//第1个线程 - 窗口
    }
}
//实现接口方式,使用synchronized实现线程同步
class SellTicket03 implements Runnable {
    private int ticketNum = 100;//让多个线程共享 ticketNum
    private boolean loop = true;//控制run方法的变量

    Object object = new Object();

    //6. 同步方法(静态的)的锁为当前类本身
    //解读
    //1. public synchronized static void m1() {} 锁是加在 SellTicket03.class
    //2. 如果在静态方法中,如果要实现一个同步代码块
    /*
            synchronized (SellTicket03.class) {
            }
     */
    public synchronized static void m1() {

    }
    public static void m2() {
        synchronized (SellTicket03.class) {

        }
    }

    //说明
    //1. public synchronized void sell() {} 就是一个同步方法
    //2. 这时锁就在this对象
    //3. 也可以在代码块上写 synchronized ,同步代码块,互斥锁还是在this对象
    public /*synchronized*/ void sell() {//同步方法,在同一时刻,只能有一个线程来执行run方法

        //同步方法(非静态的)的锁可以是this,也可以是其他对象(但要求是同一个对象)
        synchronized (/*this*/ object) {
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                loop = false;
                return;
            }
            //休眠50毫秒,模拟人休息
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" +
                    " 剩余票数=" + (--ticketNum));
        }
    }

    @Override
    public void run() {
        while (loop) {
            sell();
        }
    }
}


//使用Thread方式
//new SellTicket01.start()
//new SellTicket01.start()
class SellTicket01 extends Thread {

    private static int ticketNum = 100;//让多个线程共享 ticketNum

    public void m1() {
        synchronized (this) {

        }
        System.out.println("hello");
    }

    @Override
    public void run() {
        while (true) {
            if (ticketNum <= 0) {
                System.out.println("售票结束...");
                break;
            }
            //休眠50毫秒,模拟人休息
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("窗口 " + Thread.currentThread().getName() + " 售出一张票" +
                    " 剩余票数=" + (--ticketNum));
        }
    }
}

注意事项和细节

  1. 同步方法如果没有使用static修饰:默认锁的对象为this
  2. 同步方法如果使用static修饰:默认锁的对象为 当前类.class
  3. 实现的落地步骤
  • 需要先分析上锁的代码

  • 选择同步代码块或同步方法

  • 要求多个线程的锁对象为同一个

  • 不是同一个对象,会抛出异常

img

img

线程的死锁

  • 基本介绍

多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程中一定要避免死锁的发生

  • 案例

妈妈:你先完成作业,我让你玩手机

小明:你先让我玩手机,我再完成作业

package com.zanedu.syn;

//模拟线死锁
public class DeadLock_ {
    public static void main(String[] args) {
        //模拟死锁现象
        DeadLockDemo a = new DeadLockDemo(true);
        a.setName("A线程");
        DeadLockDemo b = new DeadLockDemo(false);
        b.setName("B线程");
        a.start();
        b.start();
    }
}

//线程
class DeadLockDemo extends Thread {
    static Object o1 = new Object();//保证多线程,共享一个对象,这里使用static
    static Object o2 = new Object();
    boolean flag;

    public DeadLockDemo(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        //分析
        //1. 如果flag 为 T,该线程A就会先得到/持有 o1 对象锁,然后尝试去获取 o2 对象锁
        //2. 如果线程A得不到 o2 对象锁,就会Blocked
        //3. 如果flag 为 F,线程B就会先得到/持有 o2 对象锁,然后回去尝试获取 o1 对象锁
        //4. 如果线程B拿不到 o1 对象锁,就会Blocked
        if (flag) {
            synchronized (o1) { //对象互斥锁,下面就是同步代码
                System.out.println(Thread.currentThread().getName() + "进入1");
                synchronized (o2) {
                    System.out.println(Thread.currentThread().getName() + "进入2");
                }
            }
        } else {
            synchronized (o2) {
                System.out.println(Thread.currentThread().getName() + "进入3");
                synchronized (o1) {
                    System.out.println(Thread.currentThread().getName() + "进入4");
                }
            }
        }
    }
}

img

释放锁

下面操作会释放锁

  1. 当前线程的同步方法、同步代码块****执行结束

案例:上厕所,完事出来

  1. 当前线程在同步代码块、同步方法种****遇到break、return

案例:没有正常的完事,经理叫他修改bug,不得已出来

  1. 当前线程在同步代码块、同步方法种****出现了未处理的Error和Exception,导致异常结束

案例:没有正常的完事,发现忘带纸,不得已出来

  1. 当前线程在同步代码块、同步方法种****执行了线程对象的wait()方法,当前线程暂停,并释放锁

案例:没有正常完事,觉得需要酝酿下,所以出来等会再进去

下面操作不会释放锁

  1. 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁

案例:上厕所,太困了,在坑位上眯了一会

  1. 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁

提示:尽量避免使用suspend()和resume()来控制线程,因为方法不再推荐使用,被弃用了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

itzzan

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值