小东吖 之 java 多线程

   // 多线程
    // 进程 是系统进行资源分配和调度的基本单位
    // 线程 系统独立调度和分派CPU的基本单位指令运行时的程序的调度单位
    // 多线程: 因计算机的硬件支持能够在同一时间执行多个线程的开发技术

    // 实现多线程的两个方法
    // 1. 继承Thread类 重写run方法 创建实现类的对象 开启线程
    // 2. 实现Runnable接口 重写run方法 创建实现类对象 分配线程并开启
// 1. 继承Thread类 重写run方法 创建实现类的对象 开启线程
// 比如三个人同时说三句祝福语
// 继承Thread方法 
public class Demo02 extends Thread{
    // 线程名 
    public Demo02(String nString) {
        super(nString);
    }

    // 重写run方法
    // 需要执行的代码
    @Override
    public void run() {
        // 获取当前线程名
        String ctName = Thread.currentThread().getName();
        // 三遍祝福语
        for (int i = 0; i < 3; i++) {
            System.out.println(ctName +": 生日快乐");
        }
    }
}


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


        // 创建实现类的对象
        Demo02 e1 = new Demo02("小明");
        Demo02 e2 = new Demo02("小红");
        Demo02 e3 = new Demo02("小白");

        // 实现类对象 开辟线程
        e1.start();
        e2.start();
        e3.start();

    }
}

2.卖票的例子

// 2. 实现Runnable接口
// 有三个窗口进行卖票 三个窗口对票进行操作 java类作为同一资源时
// 将此类情况的java类定义为Runnable接口的实现类
// 以供多个线程共同特有

// 实现Runnable接口
public class Demo01 implements Runnable{
    // 票的数量
    private int ticketNum;
    // 固定总票的数量
    int ticketSum = 0;
    // 外界控制票数 开放对外访问的接口
    public Demo01() {
        this.ticketNum = 20;
        ticketSum = getTicketNum();
    }

    public int getTicketNum() {
        return ticketNum;
    }
    public void setTicketNum(int ticketNum) {
        this.ticketNum = ticketNum;
    }

    // 重写run方法
    @Override
    public void run() {
        sellTicket();

    }
    // 卖票的方法
    private void sellTicket() {
      while (true) {
            synchronized (this) {
                if (ticketNum > 0) {
                    // 获取当前线程名
                    String ctName = Thread.currentThread().getName();
                    // 抢到一张票
                    int count = ticketSum - ticketNum + 1;
                    System.out.println(ctName + ",抢到了第" + count + "张票");
                    // 卖一张少一张
                    ticketNum--;
                    System.out.println(ctName + "卖出一张,还剩余" + ticketNum + "张票");
                }

        }
        if (ticketNum <= 0) {
            break;
        }
        Thread.yield();

    }

}
}

3.使用线程管理做家务

衣服类
public class Clothes implements Runnable{
    // 衣服干净度
    // 1. 内外都需要访问--public static
    // 2. 多个线程需要访问 -- volatile
    // volatile: 在多个线程访问一个资源时 多个线程中的任意一个线程修改了
    // 资源值,会讲资源修改信号和修改值马上同步给其他所有访问线程
    public static volatile boolean isClean;
    public static volatile boolean isOver;
    @Override
    public void run() {
        // 1. 获取线程名
        String ctName = Thread.currentThread().getName();

        // 2. 根据线程名决定执行的逻辑
        // 洗衣线程 - 洗衣
        if (ctName.equals("洗衣线程")) {
            xy();
        }
        // 晾衣线程 - 晾衣
        if (ctName.equals("晾衣线程")) {
            ly();
        }
    }

    private void xy() {
        System.out.println("洗衣机开始洗衣,请稍等");
        // 洗衣5s: 依次洗衣中 间隔1s打印一次
        for (int i = 0; i < 5; i++) {
            // 等待一秒要做的事情
            long time = System.currentTimeMillis();
            while (System.currentTimeMillis() - time <1000) {}
            System.out.println("洗衣中...");
        }

        System.out.println("洗衣机洗衣完成");
        // 将衣服状态置为"干净"
        isClean = true;
    }
    private void ly() {
        System.out.println("保姆开始晒衣,请稍等");
        // 晒衣2s: 依次晒衣中 间隔1s打印一次
        for (int i = 0; i < 2; i++) {
            // 等待一秒要做的事情
            long time = System.currentTimeMillis();
            while (System.currentTimeMillis() - time <1000) {}
            System.out.println("晾衣中...");
        }

        System.out.println("保姆晾衣完成");
        isOver = true;
    }
}

扫地类
// 扫地类
public class SD extends Thread{
    // 扫地是否完事
    public static volatile boolean isOver;
    // 构造器--线程名
    public SD(String threadName) {
        super(threadName);
    }
    @Override
    public void run() {
        System.out.println("保姆开始扫地,请稍等");
        // 扫地3s: 依次扫地中 间隔1s打印一次
        for (int i = 0; i < 10; i++) {
            // 等待一秒要做的事情
            long time = System.currentTimeMillis();
            while (System.currentTimeMillis() - time <1000) {}
            System.out.println("扫地中");
        }
        System.out.println("保姆扫地完成");
        isOver = true;

    }
}

保姆类
public class BM extends Thread{


    @Override
    public void run() {
        // 创建共有的衣服资源
        Clothes clothes = new Clothes();
        //  为衣服资源分配线程 - 洗衣线程 晾衣线程
        Thread xyThread = new Thread(clothes, "洗衣线程");
        Thread lyThread = new Thread(clothes, "晾衣线程");
        System.out.println("保姆来到洗衣间");
        xyThread.start();

        SD sd = new SD("扫地线程");
        System.out.println("保姆来到客厅");
        sd.start();
        // 等待衣服洗完 及 扫地完事
        // 刚开始为false 取非为true 在走循环 变为true时 取非 false不走循环 执行下面的语句
        while (!Clothes.isClean || !SD.isOver) {}

        System.out.println("保姆来到晾衣间");
        lyThread.start();

        while (!Clothes.isOver) {}
        System.out.println("家务处理完毕");

    }
}

测试类
// 做家务 Runnable & Thread
public class Thread05 {
    // 主人为主线程
        public static void main(String[] args) {
            System.out.println("主人发现屋子中有家务待做");
            System.out.println("于是吩咐保姆开始做家务");
            BM bm = new BM();
            System.out.println("保姆开始做家务了>>>>");
            bm.start();
            System.out.println("主人开始玩耍");
        }
}

总结

总结同步锁synchronized

synchronized 同步锁
1. synchronized作为方法的关键词
     1) 该方法为同步方法: 
     目的: 方法同一时刻只能被一个线程锁调用
     应用场景: 方法功能单一,且方法整体需要做同步处理
2. synchronized同步代码块
     1) 代码块为同步代码块
     目的: 代码块同一时刻只能被一个线程锁使用
     应用场景: 方法功能逻辑较多,方法需局部做同步处理

3. synchronized同步静态代码块 synchronized (mutex) 互斥锁
    参数 mutex: 唯一的任意对象
    通常互斥锁标识:
    static方法中 当前类.classclass对象
    成员方法: this
    this不唯一时 : static final Object
    例如: private static final Object mutex = new Object();
线程管理总结

jion 比如: t.join();使调用线程t在此之前执行完毕。 t.join(1000);等待t线程,等待时间是1000毫秒

sleep 当前正在执行的线程休眠(暂停执行

System.currentTimeMills的方法应用 延迟打印 (等待一秒要做的事情)

4.优先权最高问题

C优先级最高 最先打印(A B 需等待)
C打印完毕后,A B挣CPU执行权 逐一打印

 public class ThreadABC implements Runnable{
    // C优先级最高 最先打印(A B 需等待)
    // C打印完毕后,A B挣CPU执行权 逐一打印
    @Override
    public void run() {
        String ctName = Thread.currentThread().getName();
         锁池操作
         synchronized (锁标识): 创建锁池区域 会拥有对应的等待队列
         wait: 在所属的锁池区域(在同步代码块中),通过锁标识可进入 等待队列
         notify: 在所属的锁池区域(在同步代码块中),通过标识去等待队列唤醒一个等待线程(无法唤醒不指定的线程)
         notifyAll:在所属锁池区域(在同步代码块中) 通过标识去等待队列唤醒所有线程
        synchronized (this) {
            if (!ctName.equals("线程C")) {
                try {
                    this.wait(); // 进入等待池
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {

                }
                System.out.println("我是 " + ctName);
            }
            this.notify(); // 去等待池唤醒下一个等待的线程
        }
    }
}

测试类
public class TestABC {
    public static void main(String[] args) {
        ThreadABC abc = new ThreadABC();
        Thread a = new Thread(abc, "线程A");
        Thread b = new Thread(abc, "线程B");
        Thread c = new Thread(abc, "线程C");

        a.start();
        b.start();
        c.start();
    }
}

总结

synchronized (锁标识): 创建锁池区域 会拥有对应的等待队列
wait: 在所属的锁池区域(在同步代码块中),通过锁标识可进入 等待队列
notify: 在所属的锁池区域(在同步代码块中),通过标识去等待队列唤醒一个 等待线程(无法唤醒不指定的线程)
notifyAll:在所属锁池区域(在同步代码块中) 通过标识去等待队列唤醒所有线程

5.计数应用

public class CouutErr extends Thread{
    // 计数
    public int count;
    // 暂停
    public static volatile boolean pause;
    @Override
    public void run() {
        /*
         * 线程中断检查
         * 1. Thread.interrupted(): 检查当前线程的中断状态 如果发现线程 
         * 中断后(中断状态为true),该操作会清除中断状态
         * 2. this.isInterrupted() 检查某线程的中断状态 如果发现线程
         * 中断后(中断状态为true),该操作不会清除中断状态 
         * interrupted相关操作 与 sleep join操作
         * 
         * 对于同一线程操作而言 不建议interrupt操作与sleep join操作结合使用
         * 原因: 某线程发生interrupt操作时 会被该线程的sleep join操作捕获InterruptedException异常
         *       且当前现在的中断状态也会被清除
         * 注: A线程interrupt操作 不会影响到B线程的sleep join操作
         */
        while (!isInterrupted()) {
            if (!pause) {
                // 200ms计数一次
                // 线程自身不能使用sleep操作
//              try {
//                  Thread.sleep(200);
//              } catch (InterruptedException e) {
//                  e.printStackTrace();
//              }
                long time = System.currentTimeMillis();
                while (System.currentTimeMillis() - time < 200) {}
                System.err.println(count++);
            }

        }
    }
}

public class CountOut extends Thread{
    private int count;
    // 暂停
    public static volatile boolean pause;
    // 结束
    public static volatile boolean over;
    // 线程要执行的语句
    @Override
    public void run() {
        // 不结束一直计数
        while (!over) {
            // 暂停停止计数 否则继续计数
            if (!pause) {
                // 200ms计数一次
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 计数
                System.out.println(++count);
            }
        }
    }
}

// 线程管理类(外部) 控制 管理的线程(内部) 开启及结束
public class Thread06 {
    private static void sleep(long ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {

        }
    }

    public static void main(String[] args) {
        // out计数器 开始 暂停 结束
        //fn1();
        // err计数器 开始 暂停
        fn2();
    }
    // err计数器
    private static void fn2() {
        CouutErr err = new CouutErr();
        // 开启计数器
        err.start();
        // 2s后中断err计数器
        sleep(2000);
        CouutErr.pause = true;
        // 0.9s重新开启out计数
        sleep(900);
        CouutErr.pause = false; 
        // 在一秒计数
        sleep(1000);
        err.interrupt();
    }

    // out计数器 开始 暂停 结束
    private static void fn1() {
        // 生成out计数器并开启线程
        CountOut cOut = new CountOut();
        cOut.start();
        sleep(2000);
        CountOut.pause = true;
        // 0.5秒重新开启计数
        sleep(900);
        CountOut.pause = false;
        // 在1秒后结束out计数
        sleep(1000);
        CountOut.over = true;
    }
}

总结

线程中断检查
     1. Thread.interrupted(): 检查当前线程的中断状态 如果发现线程 
     中断后(中断状态为true),该操作会清除中断状态
     2. this.isInterrupted() 检查某线程的中断状态 如果发现线程
     中断后(中断状态为true),该操作不会清除中断状态 
     interrupted相关操作 与 sleep join操作

     对于同一线程操作而言 不建议interrupt操作与sleep join操作结合使用
     原因: 某线程发生interrupt操作时 会被该线程的sleep join操作捕获InterruptedException异常
          且当前现在的中断状态也会被清除
     注: A线程interrupt操作 不会影响到B线程的sleep
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值