多线程(基础)

1. 线程的声明周期

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

image-20230918202710662

1.2 线程状态转换图

image-20230918202750689

package com.xjz.state_;

/**
 * @author xjz_2002
 * @version 1.0
 */
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);
        }

    }
}
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;
        }
    }
}

2. 线程的同步

2.1 Synchronized 线程同步机制

image-20230918203104104

image-20230918203155198

使用 synchronized 来解决超卖问题

package com.xjz.syn;

/**
 * @author xjz_2002
 * @version 1.0
 * 使用多线程,模拟三个窗口同时售票 100 张 ==》 同步锁 synchronized 防止超卖
 */
public class SellTicket {
    public static void main(String[] args) {

        SellTicket03 sellTicket03 = new SellTicket03();

        new Thread(sellTicket03).start(); //第 1 个线程-窗口
        new Thread(sellTicket03).start(); //第 2 个线程-窗口
        new Thread(sellTicket03).start(); //第 3 个线程-窗口
    }
}

// 实现 Runnable 接口,使用 synchronized 实现线程同步
class SellTicket03 implements Runnable {

    private int ticketNum = 100;//让多个线程共享 ticketNum
    private boolean loop = true;

    public synchronized void sell() { //同步方法,在同一时刻,只能有一个线程来执行 sell方法

        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();
        }
    }
}


image-20230918203206829

3. 互斥锁

image-20230918211623420

package com.xjz.syn;

/**
 * @author xjz_2002
 * @version 1.0
 * 使用多线程,模拟三个窗口同时售票 100 张 ==》 同步锁 synchronized 防止超卖
 */
public class SellTicket {
    public static void main(String[] args) {

        SellTicket03 sellTicket03 = new SellTicket03();

        new Thread(sellTicket03).start(); //第 1 个线程-窗口
        new Thread(sellTicket03).start(); //第 2 个线程-窗口
        new Thread(sellTicket03).start(); //第 3 个线程-窗口
    }
}

// 实现 Runnable 接口,使用 synchronized 实现线程同步
class SellTicket03 implements Runnable {

    private int ticketNum = 100;//让多个线程共享 ticketNum
    private boolean loop = true;//控制 run 方法变量
    Object object = new Object();

    //同步方法(静态的)的锁为当前类本身
    //代码解读
    //1. public synchronized static void m1() {} 锁是加在 SellTicket03.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));
        }
    }

    @Override
    public void run() {

        while (loop) {
            sell();
        }
    }
}

3.1 注意事项和细节

image-20230918212125524

3.2 守护线程 setDaemon()方法

package com.xjz.method;

/**
 * @author xjz_2002
 * @version 1.0
 * 守护线程
 */
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++) {
            System.out.println("宝强在辛苦的工作..");
            Thread.sleep(1000);
        }
    }
}

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

        }
    }
}

4. 线程的死锁

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

  • 应用案例
  • 妈妈:你先完成作业,才让你玩手机
  • 小明:你先让我玩手机,我才完成作业
package com.xjz.syn;

/**
 * @author xjz_2002
 * @version 1.0
 * 模拟死锁线程
 */
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) {//这里获得 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");
                }
            }
        }
    }
}

5. 释放锁

  • 下面操作释放锁

image-20230918213412654

  • 下面操作不会释放锁

image-20230918213452338

6. 课后练习

image-20230919100120464

package com.xjz.homework;

import java.util.Scanner;

/**
 * @author xjz_2002
 * @version 1.0
 */
public class Homework01 {
    public static void main(String[] args) {
        A a = new A();
        B b = new B(a);
        a.start();
        b.start();
    }
}

/*
    (1) 在 main方法中启动两个线程
    (2) 第 1 个线程循环随机打印 100以内的整数
    (3) 直到第 2 个线程从键盘读取了 “Q" 命令。
 */

class A extends Thread {
    private boolean loop = true;

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

    @Override
    public void run() {
        while (loop) {
            int intNum = (int) (Math.random() * 100) + 1;
            System.out.println("A 线程" + intNum);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("a线程 退出");
    }
}

// 直到第 2 个线程从键盘读取了 “Q" 命令。
class B extends Thread {
    private A a;
    private Scanner scanner = new Scanner(System.in);

    public B(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.setLoop(false);
                break;
            }
        }
        System.out.println("b线程 退出");
    }
}

image-20230919100137611

package com.xjz.homework;

/**
 * @author xjz_2002
 * @version 1.0
 */
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. 有两个用户分别从同一个卡上取钱(总额:10000)
//2. 每次都取 1000,当余额不足时,就不能取款了
//3. 不能出现 超取 线程 =》 线程同步问题
class T implements Runnable {
    private static int balance = 10000;

    @Override
    public void run() {

        while (true) {

            //代码解读
            //1. 这里使用 synchronized 实现了线程同步
            //2. 当多个线程执行到这里时,就会去争夺 this 对象锁
            //3. 哪个线程争夺到(获取) this 对象锁,就执行 synchronized 代码块,执行完后,会释放 this对象锁
            //4. 争夺不到 this对象锁,就 blocked,准备继续争夺
            //5. this 对象锁是非公平锁

            synchronized (this) {
                //判断余额是否够
                if (balance < 1000) {
                    System.out.println("余额不足,无法取款");
                    break;
                }
                //取钱
                balance -= 1000;
                System.out.println(Thread.currentThread().getName() + "取出 1000,剩余" + balance);
            }
            //休眠 500ms
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

xjz_2002

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

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

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

打赏作者

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

抵扣说明:

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

余额充值