Java-线程的使用

Java-线程的使用

一、创建线程

方式一:继承Thread类,重写run方法;

class Cat extends Thread{//继承Thread类
    @Override
    public void run() {//重写run方法,实现自己的业务逻辑
        //super.run();
        //父类Thread类实现了Runnable接口中的run方法
        System.out.println("当前线程:" + Thread.currentThread().getName());

        int count = 0;
        while(count<=80) {
            System.out.println("喵喵,我是小猫咪。====" + count);
            count++;
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Thread02{
    public static void main(String[] args) {
        /**
         *  1.Thread.java程序运行起来后,就是一个进程;
         *  2.进程先启动main线程;
         *  3.main线程中又启动了一个新的子线程cat:
         *      1)子线程cat运行的同时 ,main线程也在继续往下执行;
         *  4.当主线程结束后:
         *      1)若还有子线程在运行,进程不会结束;
         *      2)只有当所有线程都结束后,进程才会结束;
         */
        Cat cat = new Cat();
        cat.start();//启动一个线程

        for (int i = 0; i < 50; i++) {
            System.out.println("main线程执行====" + i);
        }
    }
}

方式二:实现Runnable接口,重写run方法;

class A implements Runnable{
    @Override
    public void run() {
        int count = 0;
        while(count < 10){
            count++;
            System.out.println("hi == " + count);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

public class Thread03{
    public static void main(String[] args) {
        A a = new A();
        new Thread(a).start();
        //使用了设计模式中的代理模式,Thread相当于一个代理,Runnable接口中没有start()方法;

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("等价于这样!");
            }
        }).start();
    }
}

问题:为什么启动线程是调用start(),而不是直接调用run()方法;
  • 1.直接在main线程中调用run()方法将不会创建新的子线程,可以通过打印线程名称证明;
  • 2.start()方法实现原理:

在这里插入图片描述

  • 3.在JVM中,start()方法调用了start0()方法,start0()方法在不同的操作系统中有不同的实现(底层是C/C++实现的);
  • 4.因此,真正实现多线程的是start0()方法,而不是start();

两种方式的区别

  • 没有本质上的区别;

  • 实现Runnable接口方式:

    • 更适合多个线程共享同一个资源的情况;(即让多个线程执行同一个run方法)
    • 避免了单继承的限制(如果继承了Thread类就不能再去继承其他类了);
  • 因此建议使用实现Runnable接口方式来创建线程。

  • 线程类(即继承了Thread的类)中所有的变量和常量都是线程共享的。为了不导致线程安全问题,应该避免修改这些共享的变量,或者使用synchronized关键字等方案来保证线程安全。

二、终止线程

两种情况

  • 情况1:当线程完成任务后,会自动退出;
  • 情况2:使用变量来控制run()方法退出,从而终止线程,即通知方式;

案例

// 需求:启动一个线程t, 要求在main线程中去停止线程t;

class A implements Runnable{
    private boolean loop = true;//控制run()方法退出的变量
    private int n = 0;

    @Override
    public void run() {
        while(loop) {
            System.out.println("线程: " + Thread.currentThread().getName() + "执行中。。。" + "====" + (++n));
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public void setLoop(boolean loop) {//对外提供set方法,改变loop的值,从而控制run方法退出
        this.loop = loop;
    }
}

public class Thread_stop01 {
    /**
     * 需求:启动一个线程t, 要求在main线程中去停止线程t;
     * 思路:使用变量控制run()方法退出;
     */
    public static void main(String[] args) {
        A a = new A();
        Thread t = new Thread(a);
        t.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("线程: " + Thread.currentThread().getName() + "执行中。。。" + "====" + i);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if(i == 14)
                break;
        }
        a.setLoop(false);
    }
}

三、线程常用方法

第一组

  • **setName() ** :设置线程名称;
  • **getName() ** :返回线程名称;
  • **start() ** :启动线程,JVM调用该线程的start0()方法;
  • **run() ** ://调用线程类对象的run()方法;
  • **setPriority() ** :更改线程的优先级;
  • **getPriority() ** :返回线程的优先级;
  • **sleep() ** :使当前正在执行的线程暂停执行(休眠)一定毫秒数;
  • **interrupt() ** :中断线程;

第二组

  • **yield() ** :线程的“礼让”,该线程“礼让”其他线程先执行;

    • 但是,礼让的时间不确定,因此也不一定成功;(比如CPU资源并不紧张时,可以进行兼顾,则不会礼让)
    1.当一个线程调用 yield() 方法时,它会释放 CPU 资源,并将自己置于就绪状态,让系统调度器重新选择一个线程来执行。
    2.如果没有其他线程处于就绪状态,那么当前线程将继续执行。
    3.如果有其他线程处于就绪状态,那么系统调度器将会选择其中一个线程来执行,而当前线程则重新进入就绪状态,等待下一次被系统调度器选择执行。
    4.需要注意的是,调用 yield() 方法并不会让线程进入阻塞状态,因此它不会释放锁或者资源。此外,yield() 方法也不能保证当前线程被重新选择执行的机会,因为它只是一种建议,而不是强制性的要求。
    
  • **join() ** :线程的插队;

    • 插队的线程,一旦插队成功,则肯定先执行完插入线程的所有任务;
    下面是 join() 方法的执行过程:
    
    当线程 A 调用线程 Bjoin() 方法时,线程 A 将会进入阻塞状态,并等待线程 B 终止。
    1.如果线程 B 已经终止了,那么线程 A 将会从阻塞状态中恢复,并继续执行下去。
    2.如果线程 B 没有终止,那么线程 A 将会等待,直到线程 B 终止或者等待的时间到达指定的毫秒数。
    3.如果线程 B 终止了,那么线程 A 将会从阻塞状态中恢复,并继续执行下去。
    4.如果线程 B 没有在指定的时间内终止,那么线程 A 将会从阻塞状态中恢复,并继续执行下去。
    
        需要注意的是,当线程 A 调用线程 Bjoin() 方法时,如果线程 B 没有启动,那么线程 A 将会一直阻塞,直到线程 B 启动并终止。
        此外, join() 方法也可以带一个参数,用于指定线程等待的时间,如果线程 B 在等待的时间内没有终止,那么线程 A 将会从阻塞状态中恢复,并继续执行下去。
    

用户线程和守护线程

  • 用户线程

    • 也叫工作线程,当线程的任务执行完成时结束或者以通知方式结束;
  • 守护线程

    • 一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束;
    • 最常见的守护线程:垃圾回收机制;
    //将一个线程设置为守护线程
    MyDeamonThread myDeamonThread = new MyDeamonThread();
    //设置为守护线程,再启动
    myDeamonThread.setDaemon(true);
    myDeamonThread.start();
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

SEA-365

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

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

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

打赏作者

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

抵扣说明:

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

余额充值