栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java 多线程,线程中断、线程插队、守护线程、线程同步、线程互斥锁、线程死锁、释放锁

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java 多线程,线程中断、线程插队、守护线程、线程同步、线程互斥锁、线程死锁、释放锁

目录

并发、并行

实现多线程的条件:

多线程主要方法:

线程终止:

线程插队:

守护线程:

线程的七大状态:

线程同步:

互斥锁:

线程死锁:

释放锁:

多线程练习:


并发、并行

java进程中涉及到两个线程工作的原理:1.并发,2.并行

1.并发:

并发指单核cpu交替执行多个线程,当然不排除多核cpu在资源紧张的时候,调用一个cpu 去并行多个线程

2. 并行:

并行就是多核cpu 分别调用相应个数的cpu去执行多个线程

那就用下面一段代码来查看一下你电脑的cpu 个数吧:

package xiancheng.bingfa.com;

public class CpuNum {
    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();
        // 获取当前电脑CPU个数
        int cpu=runtime.availableProcessors();
        System.out.println("当前电脑CPU个数为:"+cpu);
    }
}

我们知道在使用对象去调用相应的方法时必须要等当前方法执行完毕后才会开始执行后面的代码,并且不能同时使多个方法在同一时间执行,那么多线程就可以做到同时执行多个线程,让我们来看看实现多线程需要做到哪些条件吧:(相应解释和注意事项我已写如代码块中,便于大家解读)

实现多线程的条件:
package xiancheng.bingfa.com;

import org.junit.jupiter.api.Test;

// 由于java 类只能实现单继承,当一个类已经继承了其他类时就不能再继承Thread 类来实现多线程,
// 因此这里可以实现Runnable 接口来实现多线程
public class Runnable_Test {
    public static void main(String[] args) {
        System.out.println("Test");
    }

    @Test
    public void Test01(){
        Dog dog = new Dog();
        // 此时不能再 dog.start();
        // 创建一个Thread 对象,把实现Runnable接口的dog对象放进去
        Thread thread = new Thread(dog);
        // 这里底层使用了设计模式【静态代理模式】
        thread.start();
    }

    @Test
    public void Test02(){
        Dog dog = new Dog();
        Proxy proxy = new Proxy(dog);
        proxy.strat();
    }

    @Test
    public void Test03(){
        Tiger tiger = new Tiger();
        Proxy proxy = new Proxy(tiger);
        proxy.strat();
    }
}

class Animal{}
class Tiger extends Animal implements Runnable{

    @Override
    public void run() {
        System.out.println("老虎嗷嗷叫  "+Thread.currentThread().getName());
    }
}

class Dog implements 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;
            }
        }
    }
}

实现Runnable 接口是实现多线程的一种方法,那接下来就让我们来看看另一种方法吧:

package xiancheng.bingfa.com;

public class Thread_Runnable {
    public static void main(String[] args) {
        // 创建两个线程,一个线程每隔一秒输出"Hello World",输出10次,退出,另一个线程每隔一秒输出"Hi",输出5次退出

        A a = new A();
        a.start();// 启动一个线程
        B b = new B();
        b.start();// 启动另一个线程
    }
}


class A extends Thread{
    private int i=0;

    @Override
    public void run() {
        while (true){
            System.out.println("Hello World  "+(++i)+Thread.currentThread().getName());
            // 使程序暂停1秒
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (i==10){
                break;
            }
        }
    }
}

class  B extends Thread{
    private int count;

    @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==5){
               break;
           }
       }
    }
}

那通过上面大家可以发现和之前不一样的是,我们通过b.start();来启动线程,那为什么不 b.run(); 呢?接下来就来看看实现多线程的底层源码以及这个问题的解答吧:

package xiancheng.bingfa.com;

// 演示通过继承Thread 类创建线程
public class Thread_Test {// 执行该类启动进程
    public static void main(String[] args) {// 开启主线程main
        // 创建一个Cat 对象,可以当作线程使用了
        Cat cat = new Cat();
        cat.start();// 启动线程
        

        // 当main线程启动一个子线程 Thread-0,主线程不会阻塞,会继续执行,
        // 就是和方法调用不同,它不等子线程运行结束就会继续执行主线程
        System.out.println("主线程执行:"+Thread.currentThread().getName());//main
        //此时可以发现主线程与子线程是交替执行的 -> 并行(多核CPU) 如果单核CPU则为并发
        // 注意: 要清楚的知道,主线程main结束了,进程不一定结束了(因为可能还有子线程正在进行)
        // 只有所有的线程都结束了整个进程才会结束
        for (int i=0;i<10;i++){
            System.out.println("主线程 i="+i);
            // 同样让主线程休眠
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 1. 当一个类继承了Thread 类,该类就可以当作一个线程使用
// 2. 我们会重写 run 方法,写上自己的业务逻辑
// 3. run Thread 类实现了 Runnable 接口的run方法


class Cat extends Thread{
    private int time=0;
    @Override
    public void run() {// 重写 run 方法,写上自己的业务逻辑
        // 该线程每隔一秒,在控制台输出”喵喵,我是一只小猫咪“
        while (true) {
            System.out.println("喵喵,我是一只小猫咪"+(++time)+"线程名:"+Thread.currentThread().getName());
            // 让线程休眠一秒   Ctrl+Alt+t ->异常处理快捷键
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (time==80){
                break;// 当time 到达80,就退出循环
            }
        }
    }
}

那为了让大家更容易看懂这个线程是怎样运行的,我为大家写一段代码模拟一个Thread 类,看他是如何通过调用start方法来实现多线程的:

// 代码模拟  实现Runnable 接口 开发线程的机制
// 线程代理类 模拟了一个极简单的Thread 类
class Proxy implements Runnable{// 你可以把Proxy(代理)类当作 Thread 类来使用

    private Runnable taget=null;// 设置一个Runnable 类型的属性

    public Proxy(Runnable taget) {
        this.taget = taget;
    }

    @Override
    public void run() {
        if (taget!=null){
            taget.run();
        }
    }

    public void strat(){
        strat0();// 真正实现多线程的方法
    }

    public void strat0(){
        run();
    }
}

多线程主要方法:

那下面我们来看看线程的主要方法吧:

package xiancheng.duoxiancheng;


public class Thread_method {
    public static void main(String[] args) {
        Test02();
    }

    public static void Test01(){
        // 线程常用方法: 第一组

        TDemo1 tDemo1 = new TDemo1();
        // 1.setName(String s): 设置线程名
        tDemo1.setName("马俊毅");
        // 2. setPriority(): 设置优先级
        tDemo1.setPriority(Thread.MIN_PRIORITY);
        tDemo1.start();

        // main 线程打印5句 Hi,然后中断子线程的休眠
        for (int i=0;i<5;i++){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Hi "+i);
        }
        // 3. getName(): 获取线程名
        // 4. getPriority(): 获取线程优先级
        System.out.println(tDemo1.getName()+" 优先级为:"+tDemo1.getPriority());

        // 5. interrupt(); 中断线程休眠(线程正在休眠)或者中断线程
        tDemo1.interrupt();// 当执行到这里时就会中断t 线程休眠
    }

    public static void Test02(){
        // 线程常用方法: 第二组
        

        // 1. yield(让步,放弃): 线程的礼让。让出CUP,让其他的线程执行,但礼让的时间不确定,所以也不一定成功

        // 2. join: 线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务
        // t1.join(); 人必须要等t1 执行完毕后才执行main 线程

        TDemo2 tDemo2 = new TDemo2();
        tDemo2.start();// 启动子线程

        for (int i=1;i<=20;i++){
            System.out.println("Hi");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (i==5){
                // Thread.yield();// 主线程让步(由于都是多核CPU,资源一般较为充足,因此该方法不容易成功)0
                try {
                    tDemo2.join();// 子线程插队
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

class TDemo2 extends Thread{
    private int count=0;
    @Override
    public void run() {
        while (true){
            System.out.println("Hello");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (++count==20){
                break;
            }
        }
    }
}

class TDemo1 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);
            } catch (InterruptedException e) {
                // 当线程执行到一个interrupt 方法时,就会catch 一个异常,可以加入自己的业务代码
                // InterruptedException 是捕获到一个中断异常(不是终止异常)
                System.out.println(Thread.currentThread().getName()+" 被 interrupt 了!!!");
            }
        }
    }
}

线程终止:

线程终止就是在线程执行过程中出现异常,从而终止线程

package xiancheng.duoxiancheng;

public class Thread_Stop {
    public static void main(String[] args) {
        
        T01 t01 = new T01();
        t01.start();

        // 如果希望main线程去控制t1 线程的终止,就必须可以修改loop变量
        // 让t1 退出run方法,从而终止 t1 线程 -> 通知方式
        // 先让main 线程休眠10秒,再通知t1 线程终止退出
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t01.setLoop(false);
    }
}

class T01 extends Thread{
    private int count=0;
    // 设置一个控制变量
    private boolean loop=true;

    @Override
    public void run() {
        while (loop){
            System.out.println(Thread.currentThread().getName()+" 线程正在运行中 "+(++count));
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

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

线程插队:

线程插队是告诉cpu 暂停当前执行的线程,先执行插队的线程,等插队线程执行完毕后再去执行之前的线程

package xiancheng.duoxiancheng;

// java 线程插队练习
public class Thread_join {
    public static void main(String[] args) {
        
        Tjoin tjoin = new Tjoin();
        Thread thread = new Thread(tjoin);
        for (int i=1; i<=10;i++){
            System.out.println("Hi "+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (i==5){
                thread.start();//启动子线程
                try {
                    thread.join();// 插队
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("主线程结束~~~");
    }
}

class Tjoin 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){
                System.out.println("子线程结束~~~");
                break;
            }
        }
    }
}

守护线程:

守护线程的意思就是,当一个子线程被设置为守护线程后,在主线程结束后,不管该子线程是否执行结束,都让他结束

package xiancheng.duoxiancheng;
// 用户线程和守护线程
public class Guard_Thread {
    public static void main(String[] args) {
        
        MyDaemon myDaemon = new MyDaemon();
        // 如果我们希望在主线程结束后,子线程自动结束、
        // 只需将子线程设置成一个守护线程即可
        myDaemon.setDaemon(true);
        myDaemon.start();// 必须要先设置再启动,否则会抛出异常
        for (int i=1;i<=10;i++){
            System.out.println("宝强在幸苦的工作~~~~");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class MyDaemon extends Thread{

    @Override
    public void run() {
      for ( ; ; ){// 无限循环
          System.out.println("马蓉和宋泽快乐的聊天~~~~");
          try {
              Thread.sleep(500);
          } catch (InterruptedException e) {
              e.printStackTrace();
          }
      }
    }
}

线程的七大状态:

具体的线程状态可以通过 查看jdk1.8源码中的 Enum Thread.State 枚举类。

package xiancheng.duoxiancheng;

// 线程的七大状态
// 具体的线程状态可以通过 查看jdk1.8源码中的 Enum Thread.State 枚举类
public class Thread_State {
    public static void main(String[] args) {
        MyT myT = new MyT();
        // 1. 查看状态 getState()
        System.out.println(myT.getName()+" 状态"+myT.getState());
        myT.start();

        while (Thread.State.TERMINATED!=myT.getState()){// 只要线程状态不是终止状态 就输出
            System.out.println(myT.getName()+"状态"+myT.getState());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(myT.getState());
    }
}

class MyT extends Thread{

    @Override
    public void run() {
        while (true){
            for (int i=1;i<=10;i++){
                System.out.println("Hello "+i);
                try {
                    Thread.sleep(500);// 只要调用了sleep()方法就会进入休眠状态
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            break;
        }
    }
}

那么通过上面对java 多线程机制的了解让我们来看一看下面这道题吧,看看会出什么问题呢?

package xiancheng.duoxiancheng;

import org.junit.jupiter.api.Test;

public class Thread_RunnableTest {
    public static void main(String[] args) {
        

        Test02();
    }

    public static void Test01(){
        // 继承 Thread 类实现、

        A01 a01 = new A01();// 窗口一
        A01 a02 = new A01();//窗口二
        A01 a03 = new A01();//窗口三
        a01.start();
        a02.start();
        a03.start();
        // 出现超卖现象
    }

    public static void Test02(){
        // 实现 Runnable 接口实现
        B01 b01 = new B01();
        Thread thread01 = new Thread(b01);
        Thread thread02 = new Thread(b01);
        Thread thread03 = new Thread(b01);// 资源共享
        thread01.start();
        thread02.start();
        thread03.start();
        // 同样出现超卖现象
    }
}


class A01 extends Thread{
    private int ticketNum=100;// 因为只创建一个对象,因此可以不用static 修饰
    @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 B01 implements Runnable{

    private static int ticketNum=100;// 因为票只有一百张,所以三个售票窗口共享100张票
    @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));
        }
    }
}

运行示例:

显然票数不能为负数,而且还出现了多个窗口卖出的票是同一张的问题,那是为什么呢 ?

在多个线程同时执行时,出现了多个线程同时对同一个对象(火车票)操作的情况 ,从而导致了上面问题的出现,那么该如何解决呢? 这就涉及到了我们的线程同步问题:

线程同步:
package xiancheng.duoxiancheng;

// 线程同步机制 synchronized

public class Thread_Synchronized {
    public static void main(String[] args) {
        
        // 解决卖火车票出现的问题
        Test01();
        //Test02();
    }

    public static void Test01(){
        // 继承 Thread 类实现、

        MyTest01 a01 = new MyTest01();// 窗口一
        MyTest01 a02 = new MyTest01();//窗口二
        MyTest01 a03 = new MyTest01();//窗口三
        a01.start();
        a02.start();
        a03.start();
        // 可以发现依然存在超卖问题
    }

    public static void Test02(){
        // 实现 Runnable 接口实现
        MyTest02 myTest02 = new MyTest02();
        Thread thread01 = new Thread(myTest02);
        Thread thread02 = new Thread(myTest02);
        Thread thread03 = new Thread(myTest02);// 资源共享
        thread01.start();
        thread02.start();
        thread03.start();
      //可以直接  new Thread(myTest02).start();
        // 没有再出现超卖问题
    }
}

class MyTest01 extends Thread{
    private static int ticketNum=100;// 因为票只有一百张,所以三个售票窗口共享100张票
    private static boolean loop=true;
    @Override
    public void run() {
        while (loop){
            sell();
        }
    }

    public synchronized void sell(){// 把sell方法上锁(同步方法),在同一个时刻,只允许一个线程来执行sell方法
        // 没票就退出
        if (ticketNum<=0){
            System.out.println("票已卖完!!!");
            loop=false;
        }
        else {
            // 如果还有票,休眠50毫秒  模拟现实
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口"+Thread.currentThread().getName()+"售出了一张票"+
                    "剩余票数"+(--ticketNum));
        }
    }
}

// 使用Synchronized 实现线程同步
class MyTest02 implements Runnable{

    private int ticketNum=100;// 因为只创建一个对象,因此可以不用static 修饰
    private boolean loop=true;
    @Override
    public void run() {
        while (loop){
            sell();
        }
    }

    public synchronized void sell(){// 把sell方法上锁(同步方法),在同一个时刻,只允许一个线程来执行sell方法
        // 没票就退出
        if (ticketNum<=0){
            System.out.println("票已卖完!!!");
            loop=false;
        }
        else {
            // 如果还有票,休眠50毫秒  模拟现实
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("窗口"+Thread.currentThread().getName()+"售出了一张票"+
                    "剩余票数"+(--ticketNum));
        }
    }
}

对于上面的实现Runnable 接口的Test02方法已经解决了之前的问题,但继承Thread 类的Test02 方法依然没有解决问题,那该怎么办呢? 在接下来的 互斥锁中我将为大家进行详细的讲解:

互斥锁:
package xiancheng.duoxiancheng;


public class Thread_lock {
    public static void main(String[] args) {
        Test01();
        //Test02();
    }

    

    public static void Test01(){
        // 继承 Thread 类实现、

        MyTest03 a01 = new MyTest03();// 窗口一
        MyTest03 a02 = new MyTest03();//窗口二
        MyTest03 a03 = new MyTest03();//窗口三
        a01.start();
        a02.start();
        a03.start();
        // 将方法 public synchronized static void sell(){} 设为static 后没有在出现问题
        // 因为继承Thread 类的线程,创建了三个对象,在操作线程时,方法属于类,此时的锁也是加在类上的,
        // 不同的对象在线程同步时也能受到互斥锁的限制,从而不再出现超卖现象
    }

    public static void Test02(){
        // 实现 Runnable 接口实现
        MyTest04 myTest02 = new MyTest04();
        Thread thread01 = new Thread(myTest02);
        Thread thread02 = new Thread(myTest02);
        Thread thread03 = new Thread(myTest02);// 资源共享
        thread01.start();
        thread02.start();
        thread03.start();
        //可以直接  new Thread(myTest02).start();
        // 没有再出现超卖问题
    }
}

class MyTest03 extends Thread{
    private static int ticketNum=100;// 因为票只有一百张,所以三个售票窗口共享100张票
    private static boolean loop=true;
    @Override
    public void run() {
        while (loop){
            sell();
        }
    }

    // public synchronized static void sell(){} 锁是加在类上的
    // 当然此时也可以使用代码块的互斥锁进行线程同步,但由于该方法为 static ,应将this/object 改为MyTest03.class
    public  static void sell(){// 把sell方法上锁(同步方法),在同一个时刻,只允许一个线程来执行sell方法
       synchronized (MyTest03.class) {
           // 没票就退出
           if (ticketNum <= 0) {
               System.out.println("票已卖完!!!");
               loop = false;
           } else {
               // 如果还有票,休眠50毫秒  模拟现实
               try {
                   Thread.sleep(50);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println("窗口" + Thread.currentThread().getName() + "售出了一张票" +
                       "剩余票数" + (--ticketNum));
           }
       }
    }
}

// 使用Synchronized 实现线程同步
class MyTest04 implements Runnable{

    private int ticketNum=100;// 因为只创建一个对象,因此可以不用static 修饰
    private boolean loop=true;
    Object object= new Object();
    @Override
    public void run() {
        while (loop){
            sell();
        }
    }

    //  public synchronized void sell(){}就是一个同步方法
    //1. 这时锁在 this 对象
    // 2. 也可以在代码块上写 synchronized ,同步代码块,此时的互斥锁依然是在 this 对象,
    // 当然也可以是其他对象,但必须是同一个对象
    public  void sell(){// 把sell方法上锁(同步方法),在同一个时刻,只允许一个线程来执行sell方法
       synchronized (object) {//都是同一个对象
           // 没票就退出
           if (ticketNum <= 0) {
               System.out.println("票已卖完!!!");
               loop = false;
           } else {
               // 如果还有票,休眠50毫秒  模拟现实
               try {
                   Thread.sleep(50);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println("窗口" + Thread.currentThread().getName() + "售出了一张票" +
                       "剩余票数" + (--ticketNum));
           }
       }
    }
}

那通过上面的代码及解释呢,我们已经解决了火车票超卖的问题。

线程死锁:
package xiancheng.duoxiancheng;

// 线程死锁
public class Thread_Dielock {
    public static void main(String[] args) {
        // 多线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程时一定要避免死锁的发生

        // 模拟死锁现象:
        DeadLock A = new DeadLock(true);
        DeadLock B = new DeadLock(false);
        A.setName("A线程");
        A.start();
        B.setName("B线程");
        B.start();
        
    }
}

class DeadLock extends Thread{
    static Object o1= new Object();// 保证多个线程共享一个对象
    static Object o2= new Object();
    boolean flag;

    public DeadLock(boolean flag) {// 构造器
        this.flag = flag;
    }

    @Override
    public void run() {
        // 下面业务逻辑的分析
        // 1. 如果flag 为真,线程A就会先得到/持有o1 对象锁,然后尝试去获取 o2 对象锁
        // 如果线程A得不到o2 对象锁,就会Blocked (阻塞状态)
        // 2. 如果flag 为假,线程B就会先得到/持有o2 对象锁,然后尝试去获取 o1 对象锁
        // 如果线程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){
                   System.out.println(Thread.currentThread().getName()+"进入4");
               }
           }
       }
    }
}

通过对死锁的认识,我们在编程时应该要避免死锁的产生。

释放锁:

释放锁
●下面操作会释放锁
1.当前线程的同步方法、同步代码块执行结束
2.当前线程在同步代码块、同步方法中遇到 break, return
3.当前线程在同步代码块、同步方法中出现了未处理的 ErrorException,导致异常结束
4.当前线程在同步代码块、同步方法中执行了线程对象的wait0方法,当前线程暂停,并释
放锁

●下面操作不会释放锁
1.线程执行同步代码块或同步方法时,程序调用 Thread. sleep、 Thread.yield方
法暂停当前线程的执行不会释放锁
案例:上厕所,太困了,在坑位上眯了一会
2.线程执行同步代码块时,其他线程调用了该线程的 suspend方法将该线程挂起,
该线程不会释放锁。
提示:应尽量避免使用 suspend和 resume来控制线程,方法不再推荐使用

通过以上对多线程的学习,让我们来做两道题吧:

多线程练习:

1.  在main 中启动两个线程,teat01 线程随机输出1~100 的整数,使用线程test02 控制线程test01是否结束, 直到用户输入Q结束test01 线程,随之结束test02 线程

package xiancheng.duoxiancheng;

import java.util.Scanner;

public class Synchronized_Test {
    public static void main(String[] args) {
        // 在main 中启动两个线程,teat01 线程随机输出1~100 的整数,使用线程test02 控制线程test01是否结束,
        // 直到用户输入Q结束test01 线程,随之结束test02 线程
        Test01 test01 = new Test01();
        Test02 test02 = new Test02(test01);
        test01.start();
        test02.start();
    }
}

class Test01 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("线程test01退出");
    }

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

class Test02 extends Thread{
    private Test01 test01;
    private Scanner s = new Scanner(System.in);

    public Test02(Test01 test01){
        this.test01=test01;
    }

    @Override
    public void run() {
        while (true){
            // 接收到用户输入
            System.out.println("请输入您的指令");
            char key = s.next().toUpperCase().charAt(0);
            if (key=='Q'){
                // 已通知的方式结束线程test01
                test01.setLoop(false);
                System.out.println("线程test02 结束");
                break;
            }
        }
    }
}

2.  要求: 创建两个账户,两人共同使用一张银行卡取款,起始金额为10000; 余额不足时不能再取款,更不能出现透支现象

package xiancheng.duoxiancheng;

import java.util.Scanner;

public class Synchronized_Test_ {
    public static void main(String[] args) {
        // 要求: 创建两个账户,两人共同使用一张银行卡取款,起始金额为10000; 余额不足时不能再取款,更不能出现透支现象
        
        Boank o1 = new Boank();// 人物一:李明
        o1.setName("李明");
        Boank o2 = new Boank();// 人物二:王刚
        o2.setName("王刚");
        o1.start();
        o2.start();
    }
}

class Boank extends Thread{
    private static double  count = 10000;
    private static boolean loop=true;

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

    public static void sell(){
        if (count<=0){
            System.out.println("银行卡余额不足!!!");
            loop=false;
        }
        else {
            System.out.println("请输入取款金额:");
            double s = new Scanner(System.in).nextDouble();
            while (s > count){
                System.out.println("余额不足 "+s+"n请重新输入取款金额!!!");
                s = new Scanner(System.in).nextDouble();
            }
            // 如果还有余额,休眠50毫秒  模拟现实
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "  取出金额:" +s+
                    "剩余金额:" + (count-=s));
        }
    }
}

那到这里就结束啦,有问题欢迎大家评论哦,谢谢!!!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/721058.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号