当前位置: 首页 > 图灵资讯 > 技术篇> Java多线程基础

Java多线程基础

来源:图灵教育
时间:2023-05-06 09:35:19
多线程基础线程相关概念程序(program)

它是一组用某种语言完成特定任务和编写的指令的集合。简单地说:这是我们写的代码。

进程
  1. 过程是指操作中的程序。例如,当我们使用QQ时,我们启动了一个过程,操作系统将为过程分配内存空间。当我们使用快雷并启动另一个过程时,操作系统将为快雷分配新的内存空间。
  2. 这个过程是一个程序执行过程,或者是一个正在运行的程序。这是一个动态过程:它有自己的生成、存在和消亡过程
线程
  1. 线程是由过程创建的,是过程的实体
  2. 一个过程可以有多个线程,如下图所示
其它相关概念
  1. 单线程:同时,只允许执行一个线程。
  2. 多线程:多线程可以在同一时间执行,比如一个qq过程,多个聊天窗口可以同时打开,一个快雷过程可以同时下载多个文件。
  3. 并发:同时,多个任务交替执行,造成“似乎同时”的错觉。简单来说,单核cpu实现的多个任务是并发的。

Java多线程基础_System

  1. 并行:同时执行多个任务。多核cpu可以并行实现。

Java多线程基础_主线程_02

获取cpu的数量/核心数

package com.hspedu;public class CpuNum {    public static void main(String[] args) {        Runtime runtime = Runtime.getRuntime();        ///获取当前计算机的cpu数量/核心数        int cpuNums = runtime.availableProcessors();        System.out.println(目前有cpu 个数=" + cpuNums);    }}
线程基本上使用两种方法来创建线程

使用java中线程有两种方法。

1.继承Thread类,重写run方法

2.实现Runnable接口,重写Run方法

Java多线程基础_ide_03

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

一个进程在运行程序时相当启动,一个main线程在进入main时打开,

1)请编写程序,每隔1秒打开一个线程。在控制台输出“喂食”。我是一只小猫。

2)改进上述问题:输出80次后,我是一只小猫,结束线程

3)使用JConsole 监控线程执行情况,并绘制程序示意图!

JConsole可直接输入控制台进行过程操作。

Java多线程基础_ide_04

主线程已经挂断,但子线程仍在执行中,这不会导致应用程序的结束。说明: 当main线程启动子线程时 Thread-0, 主线程不会堵塞, 此时将继续执行(执行完成后不会执行) 交替执行主线程和子线程。

package com.hspedu.threaduse;/** * 通过继承演示 Thread 类创建线程 */public class Thread01 {    public static void main(String[] args) throws InterruptedException {        //创建Cat对象,可作为线程使用        Cat cat = new Cat();        // 读源码        /*            (1)            public synchronized void start() {                start0();            }            (2)            //start0() 本地方法,JVM调用, 底层是c/c++实现            ///真正实现多线程效果, start0(), 而不是 run            private native void start0();         */        cat.start();// 启动线程-> catrun方法最终将执行        //cat.run();//run方法是一种常见的方法, 如果没有真正启动线程,run方法将在向下执行之前完成。因此,为了真正实现多线程,应该使用start方法。        //说明: 当main线程启动子线程时 Thread-0, 主线程不会堵塞, 此时将继续执行(执行完成后不会执行) 交替执行主线程和子线程。        System.out.println(继续执行主线程” + Thread.currentThread().getName());//名字main        for(int i = 0; i < 60; i++) {            System.out.println("主线程 i=" + i);            //让主线程休眠            Thread.sleep(1000);        }    }}// 说明//1. 当一类继承时 Thread 类, 这一类可以作为线程使用//2. 我们会重写 run方法,写下自己的业务代码//3. run Thread 类 实现了 Runnable 界面的run方法如下/*    @Override    public void run() {        if (target != null) {            target.run();        }    } */class Cat extends Thread {    int times = 0;    @Override    public void run() {//重写run方法,写下自己的业务逻辑        while (true) {            ////该线程每隔1秒。输出控制台 “喵喵, 我是小猫”            System.out.println("喵喵, 我是小猫”“我是小猫” + (++times) + " 线程名=" + Thread.currentThread().getName());            ////让线程休息1秒钟 ctrl+alt+t            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            if(times == 80) {                break;//当times 到80, 退出while, 这时,线程也会退出..            }        }    }}

Java多线程基础_主线程_05

start()方法调用start0()方法后,线程不一定会立即执行,只会将线程变成可操作状态。具体何时执行取决于CPU,由CPU统一调度。

线程应用案例2-Runnable实现 接口
  1. java是单继承的,在某些情况下,一个类可能已经继承了一个父类,显然不可能通过继承thread类来创建线程。
  2. java设计师提供了另一种创建线程的方式,即通过实现Runnable接口创建线程

应用案例

请每隔1秒编写一次程序。在控制台输出“hi!输出10次后,自动退出。请使用实现Runnable接口。

底层采用设计模式[代理模式]=>实现Runnnable接口开发线程的代码模拟机制

package com.hspedu.threaduse;/** * 接口Runnnable通过实现 来开发线程 */public class Thread02 {    public static void main(String[] args) {        Dog dog = new Dog();        //dog.start(); startttartt不能在这里调用        ///创建了Thread对象,把 dog对象(实现Runnable),把Thread放进去        Thread thread = new Thread(dog);        thread.start();//        Tiger tiger = new Tiger();//实现了 Runnable          // 1.//        ThreadProxy threadProxy = new ThreadProxy(tiger);          // 2.//        threadProxy.start();    }}class Animal {}class Tiger extends Animal implements Runnable {    // 6.    @Override    public void run() {        System.out.println(”老虎尖叫...");    }}///线程代理类 , 极简主义的Threadclasss模拟 ThreadProxy implements Runnable 你可以把Proxy类作为{// ThreadProxy    private Runnable target = null;//属性,类型是 Runnable    // 5.    @Override    public void run() {        if (target != null) {            target.run();///动态绑定(运行类型Tiger)        }    }    public ThreadProxy(Runnable target) {        this.target = target;    }    // 3.    public void start() {        start0();///这种方法真正实现了多线程方法    }    // 4.    public void start0() {        run();    }}class Dog implements Runnable { //通过实现Runnable接口,开发线程    int count = 0;    @Override    public void run() { //普通方法        while (true) {            System.out.println(”小狗汪汪叫..hi" + (++count) + Thread.currentThread().getName());            //休眠1秒            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            if (count == 10) {                break;            }        }    }}
线程应用案例-多线程执行

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

package com.hspedu.threaduse;/** * main线程启动两个子线程 */public class Thread03 {    public static void main(String[] args) {        T1 t1 = new T1();        T2 t2 = new T2();        Thread thread1 = new Thread(t1);        Thread thread2 = new Thread(t2);        thread1.start();//启动第一个线程        thread2.start();//启动第二个线程    }}class T1 implements Runnable {    int count = 0;    @Override    public void run() {        while (true) {            ///每隔1秒输出一次 “hello,world”,输出10次            System.out.println("hello,world " + (++count));            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            if(count == 60) {                break;            }        }    }}class T2 implements Runnable {    int count = 0;    @Override    public void run() {        ///每隔1秒输出一次 “hi”,输出5次        while (true) {            System.out.println("hi " + (++count));            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            if(count == 50) {                break;            }        }    }}
如何理解线程?

Java多线程基础_主线程_06

继承Thread vs Runnnablele实现 的区别
  1. 从java的设计来看,通过继承thread或实现Runnable接口来创建线程本质上没有区别。从jdk帮助文档中,我们可以看到thread类本身实现了Runnable接口。
  2. 实现Runnable接口更适合多线程共享一个资源,避免单继承的限制。建议使用Runnable。
  3. [售票系统],编程模拟三个售票窗口的100张售票,分别使用继承 Thread和实现 Runnable分析有什么问题? 都会出现超卖的问题。
package com.hspedu.ticket;/** * 使用多线程,模拟三个窗口同时售票100张 */public class SellTicket {    public static void main(String[] args) {        /////////测试///        Selticket01 selticket01 = new Selticket01();//        Selticket01 selticket02 = new Selticket01();//        Selticket01 selticket03 = new Selticket01();////        ///我们会在这里超卖..//        selticket01.start();/////开始售票线程//////        selticket02.start();/////开始售票线程//////        selticket03.start();///开始售票线程        System.out.println(=====================));        Selticket02 selticket02 = new Selticket02();        new Thread(selticket02).start();//第一行-窗口        new Thread(sellTicket02).start();//第二行-窗口        new Thread(sellticket02).start();//第三行-窗口    }}///使用Threadclass Selticket01 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));        }    }}///实现接口方式classs Selticket02 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));//1 - 0 - -1  - -2        }    }}
线程终止的基本说明
  1. 任务完成后,线程将自动退出。
  2. 也可以通过使用变量控制run退出来停止线程,即通知。
应用案例

要求:启动线程t,要求在main线程中停止线程t,请编程实现。

package com.hspedu.exit_;public class ThreadExit_ {    public static void main(String[] args) throws InterruptedException {        T t1 = new T();        t1.start();        // 如果希望 main 线程去控制 t1 线程终止, 必须修改 loop        // 让 t1 退出 run 从而终止方法 t1 线程 -> 通知方式        // 让主线休眠 10 秒,再通知 t1 线程退出        System.out.println(”main线程休眠10s...");        Thread.sleep(10 * 1000);        t1.setLoop(false);    }}class T extends Thread {    private int count = 0;    // 设置控制变量    private boolean loop = true;    @Override    public void run() {        while (loop) {            try {                Thread.sleep(50);// 让当前线程休眠50ms            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println("T 运行中..." + (++count));        }    }    public void setLoop(boolean loop) {        this.loop = loop;    }}
第一组常用的线程方法
  1. setName A ///设置与参数name相同的线程名称
  2. getName ////返回该线程的名称
  3. startM //使线程开始执行;Java虚拟机底层调用该线程的start0方
  4. run //调用线程对象 run方法;
  5. setPriority ////改变线程的优先级
  6. getPriority ////获取线程的优先级
  7. sleep ////让当前正在执行的线程在指定的毫秒内休眠(暂停执行)
  8. interrupt ///中断线程
注意事项和细节
  1. start底层将创建新的线程,调用run, run 它是一种简单的调用方法,不会启动新线程。
  2. 范围为线程优先级。
  3. interrupt,中断线程,但并没有真正结束线程。因此,它通常用于中断正在休眠的线程。
  4. sleep:线程的静态方法使当前线程休眠。
package com.hspedu.method;public class ThreadMethod010 {    public static void main(String[] args) throws InterruptedException {        ///测试相关方法        T t = new T();        t.setName("timerring");        t.setPriority(Thread.MIN_PRIORITY);//1        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());//1        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() + "  吃包子~~~~ + i);            }            try {                System.out.println(Thread.currentThread().getName() + " 休眠中~~~);                Thread.sleep(20000);//20秒            } catch (InterruptedException e) {                // 当该线程执行到interupt时 方法时,会catch 一个 异常, 您可以添加自己的业务代码                // InterruptedException 捕获中断异常.                System.out.println(Thread.currentThread().getName() + "被 interupt);            }        }    }}
第二组常用方法
  1. yield:线程礼让。让cpu,让其他线程执行,但礼让时间不确定,所以不一定礼让成功
  2. join:插队线程。一旦插队线程成功,插入线程的所有任务都必须首先执行。

案例:main线程创建子线程,每1s输出hello,输出20次,主线程每1秒, 输出hi,输出20次。要求:同时执行两个线程,当主线程输出5次时,让子线程完成运行,主线程继续运行。

package com.hspedu.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();// 相当于让T2 首先执行线程                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 +  " 包子");        }    }}
课堂练习
  1. 主线程每隔1秒输出hi,共10次
  2. 当输出到hi5时,启动一个子线程(要求Runnable),每1秒输出hello,等待该线程输出10次 在hello之后,退出
  3. 主线程继续输出hi,直到主线程退出.
  4. 如图所示,完成代码实际上是线程插队
package com.hspedu.method;public class ThreadMethodExercise {    public static void main(String[] args) throws InterruptedException {        Thread t3 = new Thread(new T3());///创建子线程        for (int i = 1; i <= 10; i++) {            System.out.println("hi " + i);            if(i == 5) {//说明主线程输出5次 hi                t3.start();///启动子线程 输出 hello...                t3.join();//立即将T3子线程,插入main线程,让T3先执行            }            Thread.sleep(1000);///输出一次 hi, 让main线程也休眠1s        }    }}class T3 implements Runnable {    private int count = 0;    @Override    public void run() {        while (true) {            System.out.println("hello " + (++count));            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }            if (count == 10) {                break;            }        }    }}
用户线程和守护线程
  1. 用户线程: 当线程任务完成或通知结束时,也称为工作线程。
  2. 守护线程: 一般为工作线程服务,当所有用户线程结束时,保护线程自动结束。
  3. 常见的守护线程: 垃圾回收机制。
应用案例

接下来,我们将测试如何将一个线程设置为守护线程。

只需要将 myDaemonThread.setDaemon(true); 设置为 true 即可。

package com.hspedu.method;public class ThreadMethod0 {    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 {    public void run() {        for (; ; ) {///无限循环            try {                Thread.sleep(1000);///休眠1000毫秒            } catch (InterruptedException e) {                e.printStackTrace();            }            System.out.println(“聊天”);        }    }}
生命周期线程的几种状态

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

Java多线程基础_System_07

线程状态转换图!!!

有书说有七种状态,其实就是把Runnnable状态中的Ready和Running分开。是否运行取决于核心状态的调度。

Java多线程基础_System_08

 

package com.hspedu.state_;public class ThreadState_ {    public static void main(String[] args) throws InterruptedException {        T t = new T();        System.out.println(t.getName() + " 状态 " + t.getState());        t.start();        while (Thread.State.TERMINATED != t.getState()) {            System.out.println(t.getName() + " 状态 " + t.getState());            Thread.sleep(500);        }        System.out.println(t.getName() + " 状态 " + t.getState());    }}class T extends Thread {    @Override    public void run() {        while (true) {            for (int i = 0; i < 10; i++) {                System.out.println("hi " + i);                try {                    Thread.sleep(1000);                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            break;        }    }}
Synchronized线程同步机制
  1. 在多线程编程中,不允许多线程同时访问一些敏感数据。此时,使用同步访问技术,以确保数据在任何时候都有一个线程访问,以确保数据的完整性。
  2. 也可以理解:线程同步,即当一个线程操作内存时,其他线程不能操作内存地址,直到线程完成,其他线程才能操作内存地址。
同步具体方法-Synchronized方法
synchronized (对象){ // 只有获得对象的锁,才能操作同步代码/// 需要同步代码;}
方法二方法声明

synchronized 也可以放在方法声明中,表示整个方法-同步方法

public synchronized void m (String name){//需要同步的代码}
同步原理的分析

Java多线程基础_System_09

互斥锁的基本介绍
  1. 在Java语言中,引入了对象互斥锁的概念,以确保共享数据操作的完整性。
  2. 每个对象对应一个可以称为“互斥锁”的标记,以确保在任何时候只能有一个线程访问对象。
  3. 关键字synchronized联系对象的互斥锁。当一个对象用synchronized修改时,表示对象只能在任何时候访问一个线程。
  4. 同步限制:降低程序执行效率。
  5. 同步方法**(非静态)锁可以是this或其他对象(要求是同一对象)。**
  6. 同步方法(静态)的锁是当前类本身。即类.class
package com.hspedu.syn;/** * 使用多线程,模拟三个窗口同时售票100张 */public class SellTicket {    public static void main(String[] args) {        /////////测试///        Selticket01 selticket01 = new Selticket01();//        Selticket01 selticket02 = new Selticket01();//        Selticket01 selticket03 = new Selticket01();////        ///我们会在这里超卖..//        selticket01.start();/////启动售票线程////////        selticket02.start();/////启动售票线程////////        selticket03.start();/////启动售票线程////////        System.out.println(=====================));//        Selticket02 selticket02 = new Selticket02();////        new Thread(selticket02).start();//第一线程-窗口//        new Thread(selticket02).start();//第二线程-窗口//        new Thread(selticket02).start();//第三行-窗口        ///测试一个        Selticket03 selticket03 = new Selticket03();        new Thread(selticket03).start();//第一行-窗口        new Thread(selticket03).start();//第二行-窗口        new Thread(selticket03).start();//第三行-窗口    }}//实现接口方式, 使用synchronized实现同步classss Selticket03 implements Runnable {    private int ticketNum = 100;//让多个线程共享 ticketNum    private boolean loop = true;///控制run方法的变量    Object object = new Object();    ///同步方法(静态)的锁是当前类本身    //1. public synchronized static void m1() {} 锁是加在 Selticket03.class 上    //2. 若在静态方法中,实现同步代码块.    /*        synchronized (Sellticket03.class) {            System.out.println("m2");        }     */    public synchronized static void m1() {    }    public static  void m2() {        synchronized (Sellticket03.class) {            System.out.println("m2");        }    }    //1. public synchronized void sell() {} 是同步法    //2. 这时锁在 this对象    //3. 也可以写在代码块上 synchronize ,同步代码块, 互斥锁还是this对象?    public /*synchronized*/ void sell() { //同步法, 在同一时刻, 执行sell方法只能有一个线程        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));//1 - 0 - -1  - -2        }    }    @Override    public void run() {        while (loop) {            sell();///sell方法是一种共同步法        }    }}///使用Thread//// new Sellticket01().start()// new Sellticket01().start(); 对象不一样,不锁M1()class Selticket01 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));        }    }}///实现接口方式classs Selticket02 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));//1 - 0 - -1  - -2        }    }}
注意事项和细节
  1. 如果不使用static修改同步方法,则默认锁对象为thiss
  2. 如果该方法采用static修改,则默认锁定对象:当前类别.class
  3. 着陆步骤的实现:
  • 首先需要分析锁定的代码
  • 选择同步代码块或同步方法(同步范围越小,效率越高)
  • 要求多线程的锁对象是相同的!
线程死锁的基本介绍

许多线程占用了对方的锁资源,但拒绝让步,导致死锁,在编程中必须避免死锁。

应用案例
package com.hspedu.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();// 保证多线程,共享一个对象,这里使用staticccticc    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) { // 在这里获得li对象的监控权                    System.out.println(Thread.currentThread().getName() + " 进入2");                }                            }        } else {            synchronized (o2) {                System.out.println(Thread.currentThread().getName() + " 进入3");                synchronized (o1) { // 在这里获得li对象的监控权                    System.out.println(Thread.currentThread().getName() + " 进入4");                }            }        }    }}
释放锁下面的操作释放锁
  1. 执行当前线程的同步方法和同步代码块。
  2. 当前线程在同步代码块和同步方法中遇到break、return。
  3. 未处理的Error或Exception出现在当前线程的同步代码块和同步方法中,导致异常结束。
  4. 线程对象的wait()方法在同步代码块和同步方法中执行,当前线程暂停并释放锁。
下面的操作不会释放锁
  1. 当线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()暂停执行当前线程的方法不会释放锁
  2. 当线程执行同步代码块时,其他线程调用该线程的suspend()方法悬挂该线程,该线程不会释放锁。提示:尽量避免使用suspend()和resume()来控制线程,不再推荐使用该方法
作业
  1. 编程题(1)在main方法中启动两个线程(2)在第一个线程循环中随机打印100以内的整数(3),直到第二个线程从键盘读取“Q”命令。
package com.hspedu.homework;import java.util.Scanner;public class Homework01 {    public static void main(String[] args) {        A a = new A();        B b = new B(a);//必须注意.        a.start();        b.start();    }}///创建A线程classs A extends Thread {    private boolean loop = true;    @Override    public void run() {        //输出1-100数字        while (loop) {            System.out.println((int)(Math.random() * 100 + 1));            //休眠            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }        System.out.println(a线程退出...");    }    public void setLoop(boolean loop) {//可以修改loop变量        this.loop = loop;    }}//直到第二个线程从键盘读取”Q命令class B extends Thread {    private A a;    private Scanner scanner = new Scanner(System.in);    public B(A a) {//构造器,A类对象直接传输        this.a = a;    }    @Override    public void run() {        while (true) {            ////收到用户的输入            System.out.println(”请输入您的指令(Q)表示退出:");            char key = scanner.next().toUpperCase().charAt(0);            if(key == 'Q') {                /////以通知的方式结束a线程                a.setLoop(false);                System.out.println(b线程退出.");                break;            }        }    }}
  1. 编程题(1)有两个用户分别从同一张卡上取钱(总额:1万)(2)每次取1000,余额不足时不能取钱(3)不能超取=线程同步.
package com.hspedu.homework;public class Homework02 {    public static void main(String[] args) {        T t = new T();        Thread thread1 = new Thread(t);        thread1.setName("t1");        Thread thread2 = new Thread(t);        thread2.setName("t2");        thread1.start();        thread2.start();    }}//1.因为它涉及多个线程共享资源,因此,我们使用实现Runnable的方法//2. 每次取出 1000class T implements  Runnable {    private int money = 10000;    @Override    public void run() {        while (true) {            //解读            //1. 这里使用 synchronized 线程同步已经实现            //2. 这里执行多个线程时,就会去争夺 对象锁this            //3. 哪个线程争夺到(获取)对象锁this,就执行 synchronized 代码块, 执行完后,会释放对象锁this            //4. 争夺不到对象锁this,就blocked ,准备继续竞争            //5. this对象锁是非公平锁.            synchronized (this) {//                ///判断余额是否足够                if (money < 1000) {                    System.out.println(“余额不足”);                    break;                }                money -= 1000;                System.out.println(Thread.currentThread().getName() + " 取出了1000 当前余额=“” + money);            }            //休眠1s            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}