JAVA多线程

本文介绍了Java中创建线程的两种方式:继承Thread和实现Runnable接口,并通过售票系统案例展示了线程同步的重要性。讨论了线程的生命周期、状态转换图,以及Synchronized关键字在避免死锁中的作用。此外,还提到了线程的终止方法和如何监控线程状态。

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

目录

创建线程的几种方式

1.继承Thread

star方法

实现Runnable接口

多个子线程

案例 

售票系统

 线程终止

线程常用方法

常规方法

特别方法

线程的生命周期

线程状态

线程状态转换图

Synchronized

互斥锁 

死锁 

释放锁

 案例

终止线程


jconsole 命令可以监控线程状态

创建线程的几种方式

1.继承Thread

star方法

启动star方法

进入start0——本地方法,由JVM调用

实现Runnable接口

案例

 代码

package pojo;
public class Thread2{
    public static void main(String[] args) {
        Dog dog = new Dog();
        Thread thread = new Thread(dog);
        //ThreadProxy thread = new ThreadProxy(dog);    //这是模拟
        thread.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程:" + i + "  name=" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
 class Dog implements Runnable{
    int count =0;
    @Override
    public void run() {

        while (true) {
            System.out.println("我是多线程:" + Thread.currentThread().getName() +"   "+ (++count));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (count == 10){
                break;
            }
        }
    }
}
//线程代理类--模拟Thread thread = new Thread(dog) 这种设计模式
class ThreadProxy implements Runnable{
    private Runnable target = null;
    @Override
    public void run() {
        if (target != null){
            target.run();
        }
    }
    public ThreadProxy(Runnable target) {//构造函数赋值
        this.target = target;
    }
    public void start(){
        start0();//这是实现多线程的方法
    }
    private void start0() {
        run();
    }
}

多个子线程

案例 

售票系统

 实现 

代码

package ticket;

/*
使用多线程模拟三个窗口同时售票100张
*/

public class SellTicket {
    public static void main(String[] args) {
        //Thread    会有超卖问题
//        SellTicket01 sellTicket01 = new SellTicket01();
//        SellTicket01 sellTicket02 = new SellTicket01();
//        SellTicket01 sellTicket03 = new SellTicket01();
//        sellTicket01.start();
//        sellTicket02.start();
//        sellTicket03.start();
        //Runnable
        SellTicket02 sellTicket02 = new SellTicket02();
        new Thread(sellTicket02).start();
        new Thread(sellTicket02).start();
        new Thread(sellTicket02).start();
    }

}

class SellTicket01 extends Thread{
    private   static int ticketNum = 100;//多个线程共享100

    @Override
    public  void run() {
        try {
            while (true){
                if (ticketNum <= 0){
                    System.out.println("售票结束...");
                    break;
                }
                Thread.sleep(50);//休眠50毫秒,模拟
                System.out.println("窗口"+ Thread.currentThread().getName()+"售出一张票,剩余票 " + (--ticketNum));
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

class SellTicket02 implements Runnable{
    //这里不用是静态
    private  int ticketNum = 1000;//多个线程共享1000(写大一点更能看出多个线程卖票)
    private boolean loop = true;

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

    public synchronized void sell() {//加锁,同一时间只能有一个线程操作sell方法
        if (ticketNum <= 0){
            System.out.println("售票结束...");
            loop = false;
            return;
        }
        try {
            Thread.sleep(50);//休眠50毫秒,模拟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("窗口"+ Thread.currentThread().getName()+"售出一张票,剩余票 " + (--ticketNum));
    }
}

Thread运行结果                                               Runnable运行结果

 线程终止

 说明

  1. 当线程完成任务后,会自动退出
  2. 还可以通过使用变量来控制run方法退出的方式停止线程,既通知方式

案例

代码

package ThreadExit;

public class ThreadExit {
    public static void main(String[] args) throws InterruptedException {
        T t = new T();
        t.start();
        //让 t 退出 run 方法,从而终止 t 线程 -> 通知方法   主线程通知 t 线程终止
        System.out.println("主线程休眠10s,t线程不会受影响...");
        Thread.sleep(10 * 1000);
        t.setLoop(false);//调用方法退出run
    }
}
class T extends Thread{
    //设置一个控制变量
    private boolean loop = true;
    private int count = 0;
    @Override
    public void run() {
        try {
            while (loop){
                Thread.sleep(50);
                System.out.println("线程运行 " + (++count));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //调用这个方法给loop赋值退出while循环
    public void setLoop(boolean loop) {
        this.loop = loop;
    }
}

线程常用方法

常规方法

1.setName         /设置线程名称,使之与参数 name 相同

2.getName         //返回该线程的名称
3.start         //使该线程开始执行: Java 虚拟机底层调用该线程的 start0 方法

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

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

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

7. sleep        //在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
8, interrupt         //中断线程——唤醒线程

注意事项 

特别方法

1.yield:线程的礼让。让出cpu,让其他线程执行,但礼让的时间不确定,所以也不一定礼让成功
2.join:线程的插队。插队的线程一旦插队成功,则肯定先执行完插入的线程所有的任务

        t1.join(),那么 t1 线程就先执行完再执行其他的线程。

案例

创建一个子线程,每隔1s 输出 hello,输出 20次,主线程每隔1秒,输出 hi,输出 20次.要求: 两个线程同时执行,当主线程输出 5次后,就让子线程运行完毕,主线程再继续

用户线程和守护线程

1.用户线程:也叫工作线程,当线程的任务执行完或通知方式结束
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束

        如果我们希望当main线程结束后,子线程自动结束只需将 子线程 设为守护线程即可

3.常见的守护线程: 垃圾回收机制

线程的生命周期

线程状态

官方文档有六种状态:
NEW
尚未启动的线程处于此状态。
RUNNABLE
在Java虚拟机中执行的线程处于此状态
BLOCKED
被阻塞等待监视器锁定的线程处于此状态
WAITING
正在等待另一个线程执行特定动作的线程处于此状态
TIMED WAITING

正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
TERMINATED
已退出的线程处于此状态

线程状态转换图

 

Synchronized

代码在        class SellTicket 

互斥锁 

死锁 

案例

package sync;

public class DeadLock {
    public static void main(String[] args) {
        //通过不同的 flag 参数,模拟死锁的情况
        new DeadLockDemo(true).start();
        new DeadLockDemo(false).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");
                }
            }
        }

    }
}

释放锁

下面操作会释放锁

以下操作不会释放锁

 案例

终止线程

(1)在main方法中启动两个线程

(2)第1个线程循环随机打印100以内的整数

(3)直到第2个线程从键盘读取了“Q”命令

 实现

package homework;

import java.util.Scanner;

public class Homework01 {
    public static void main(String[] args) {
        A a = new A();
        B b = new B(a);//一定要注意,传 a
        a.start();
        b.start();
    }
}

class A extends Thread {
    private boolean loop = true;

    @Override
    public void run() {//加锁之后不会超卖
        while (loop) {
            System.out.println("A线程: "+(int)(Math.random() * 100 +1));
            //休眠
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //提供一个可以修改loop的值的方法
    public void setLoop(boolean loop) {
        this.loop = loop;
    }
}

//这个线程用来接收用户输入 Q
class B extends Thread {
    //把 A 作为字段就可以得到 A的 loop 了
    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("B线程:请输入 Q 指令: ");
            char key = scanner.next().toUpperCase().charAt(0);
            if (key == 'Q'){
                //以通知的方式
                System.out.println("输入正确,将通知 A 线程终止");
                a.setLoop(false);
                System.out.println(" A 线程已终止");

                break;
            }
            System.out.println("指令错误!!!");
        }
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值