线程的基础(2)

本文介绍了Java中的线程基础知识,包括通过模拟售票系统展示线程同步问题,线程的终止方法,如通过变量通知线程退出,以及线程的常用方法如`start`,`sleep`,`interrupt`。还探讨了线程的礼让与插队行为,如`yield`和`join`的使用。最后提到了用户线程和守护线程的概念,展示了如何设置守护线程以跟随主线程结束。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

 售票系统

线程终止

应用案例 通知线程退出

线程常用方法

线程方法注意事项和细节

常用方法第二组

线程课堂练习

用户线程和守护线程


 售票系统

我们先来看看一个日常生活中的售票,日常生活中有需要用到售票的地方,但是并没有那么简单,看一段模拟售票的代码

代码演示:

注意:下面这个模拟的售票系统,是会出现超卖的情况的,因为我们没有做线程同步的处理,因此就会发生票数超卖的情况,在现实生活中,很明显这样是不可以的,所以后面我们就会对该售票系统进行处理,处理超卖的情况

package idea.chapter17.ticket;

/**
 * 使用多线程,模拟三个窗口同时售票100张
 */
public class SellTicket {
    public static void main(String[] args) {

        //测试
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
//
//        //这里我们会出现超卖..
//        sellTicket01.start();//启动售票线程
//        sellTicket02.start();//启动售票线程
//        sellTicket03.start();//启动售票线程


        System.out.println("===使用实现接口方式来售票=====");
        SellTicket02 sellTicket02 = new SellTicket02();

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


//使用继承Thread类的方式
class SellTicket01 extends Thread {

    private static int ticketNum = 100;//让多个线程共享 ticketNum  因为静态属性是共享的

    @Override
    public void run() {
        while (true) {

            //只有当剩余的票小于0才会退出
            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));//1 - 0 - -1  - -2

        }
    }
}

线程终止

基本说明

1.当线程完成任务后,会自动退出

2.还可以通过使用变量来控制run方法退出的方式停止线程,即通知方式

应用案例 通知线程退出

需求: 启动一个线程t,要求在main线程中去停止线程t,请编程实现

代码演示:

所谓的线程终止,也就是在之前的代码上,做出一点修改,之前我们while循环都是无限循环,然后设置了一个变量,当这个变量达到了一定的值之后,我们就break跳出循环,但是这种方式的话,需要把我们执行的条件,全部执行完毕,然后才会截至,

那么我们想要提前终止这个线程的话,我们需要设置一个布尔值的变量,然后初始化为true,提供set方法,在我们需要中止这个线程的时候,就把该变量设置成false就可以了

package idea.chapter17.exit_;

//求: 启动一个线程t,要求在main线程中去停止线程t,请编程实现

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 //设置线程冬称,使之与参数 name 相同

2.qetName //返回该线程的名称·

3.start //使该线程开始执行; Java 虚拟机底层调用该线程的 start0 方法

4.run //调用线程对象 run 方法

5.setPriority //更改线程的优先级

6.getPriority //获取线程的优先级

7sleep//在指定的毫秒数内让当前正在执行的线程休眠 (暂停执行)

8.interrupt //中断线程

线程方法注意事项和细节

1.start 底层会创建新的线程,调用run,run 就是一个简单的方法调用,不会启动新线程

2线程优先级的范围

3.interrupt,中断线程,但并没有真正的结束线程。所以一般用于中断正在休眠线程

4.sleep:线程的静态方法,使当前线程休眠

代码演示:

 //注意interrupt是中段线程,不是退出线程,就是把我们正在休眠的线程,唤醒,唤醒之后线程就会抛出一个这样的异常InterruptedException

package idea.chapter17.method;

/**
 * 演示线程的方法
 */

public class ThreadMethod01 {
    public static void main(String[] args) throws InterruptedException {
        //测试相关的方法
        T t = new T();
        t.setName("jack");
        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

        //注意interrupt是中段线程,不是退出线程,就是把我们正在休眠的线程,唤醒,唤醒之后线程就会抛出一个这样的异常InterruptedException
        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) {
                //当该线程执行到一个interrupt 方法时,就会catch 一个 异常, 可以加入自己的业务代码
                //InterruptedException 是捕获到一个中断异常.
                System.out.println(Thread.currentThread().getName() + "被 interrupt了");
            }
        }
    }
}

常用方法第二组

1.yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功

2.join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务

代码演示:

//注意:这里使用yield表示线程的礼让,但是不一定礼让成功,并不是固定的,但是如果是join线程插队,比如我们在main方法中调用了t2.join()
//那么main线程就会等待子线程也就是t2这个线程执行完毕在继续执行,主线程会堵塞在这里
package idea.chapter17.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("主线程 让 子线程 先吃");
                //1.yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
                //2.join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务

                //注意:这里使用yield表示线程的礼让,但是不一定礼让成功,并不是固定的,但是如果是join线程插队,比如我们在main方法中调用了t2.join()
                //那么main线程就会等待子线程也就是t2这个线程执行完毕在继续执行,主线程会堵塞在这里
                //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.先在主线程中输出hi 当主线程输出了五句hi之后,我们就马上启动子线程,注意题目要求要我们子线程全部执行完毕,才让主线程继续执行
  那么我们刚刚学的两个方法 一个是yield 方法 还有一个是join方法 这两个方法我们需要使用join方法,因为yield不一定会礼让成功
  如果使用yield方法可能导致发生主线程和子线程互相交替的执行,形成了一个并发的效果,这样就不满足我们的要求,所以需要使用join方法
  就不会导致这种情况发生了,因为join方法是线程插队,当调用了这个方法,就会优先让这个线程执行完毕,才继续执行

package idea.chapter17.method;

/*
1.主线程每隔1s,输出 hi,一共 10次
2.当输出到 hi 5 时,启动一个子线程(要求实现Runnable),每隔1s 输出 hello,等该线程输出10次 hello后,退出
3.主线程继续输出 hi,直到主线程退出
4.如图,完成代码
 */
public class ThreadMethodExercise {
    public static void main(String[] args) throws InterruptedException {
        T3 t3 = new T3();
        Thread thread = new Thread(t3);
        for (int i = 0; i < 10; i++) {
            System.out.println("hi");
            Thread.sleep(1000);
            if (i == 5) {
                thread.start();
                thread.join();
                
            }
        }
        System.out.println("主线程结束");
    }
}
/*
思路分析:
1.先在主线程中输出hi 当主线程输出了五句hi之后,我们就马上启动子线程,注意题目要求要我们子线程全部执行完毕,才让主线程继续执行
  那么我们刚刚学的两个方法 一个是yield 方法 还有一个是join方法 这两个方法我们需要使用join方法,因为yield不一定会礼让成功
  如果使用yield方法可能导致发生主线程和子线程互相交替的执行,形成了一个并发的效果,这样就不满足我们的要求,所以需要使用join方法
  就不会导致这种情况发生了,因为join方法是线程插队,当调用了这个方法,就会优先让这个线程执行完毕,才继续执行
 */

class T3 implements Runnable {
    @Override
    public void run() {
        int count = 0;
        while (true) {
            try {
                System.out.println("hello" + (++count));
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 10) {
                System.out.println("子线程结束");
                break;
            }
        }
    }
}

用户线程和守护线程

1.用户线程:也叫工作线程,当线程的任务执行完或通知方式结束

2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束3.常见的守护线程:垃圾回收机制

代码演示:

思路分析:
当我们有这样的一个需求,就是当主线程执行完毕退出之后,子线程也跟着退出,那么在正常的情况下,是不会退出的
即使主线程已经,已经销毁了,但是子线程并不会结束,还会继续的执行,那么如果想要主线程结束,子线程也跟着结束
那么就需要讲子线程设置为守护线程就可以,默认情况下我们的子线程是无线循环,一直不会退出,只需要这样设置一些myDaemonThread.setDaemon(true);
那么子线程就是守护线程,注意要先设置为守护线程在启动

package idea.chapter17.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("mary在辛苦的工作...");
            Thread.sleep(1000);
        }
    }
}
/*
思路分析:
当我们有这样的一个需求,就是当主线程执行完毕退出之后,子线程也跟着退出,那么在正常的情况下,是不会退出的
即使主线程已经,已经销毁了,但是子线程并不会结束,还会继续的执行,那么如果想要主线程结束,子线程也跟着结束
那么就需要讲子线程设置为守护线程就可以,默认情况下我们的子线程是无线循环,一直不会退出,只需要这样设置一些myDaemonThread.setDaemon(true);
那么子线程就是守护线程,注意要先设置为守护线程在启动
 */

class MyDaemonThread extends Thread {
    public void run() {
        for (; ; ) {//无限循环
            try {
                Thread.sleep(1000);//休眠1000毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("jack 和 tom 一直在聊天~~~");
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值