多线程学习第三篇

发布时间 2023-09-02 23:00:25作者: 紫宁不嗣音

3、线程状态

线程五大状态:

  • 创建状态:通过new创建线程
  • 就绪状态:通过start()启动线程进入就绪状态
  • 阻塞状态:通过CPU调配进入运行状态
  • 运行状态:在运行状态时,可以进行如sleep,wait等方法使线程进入阻塞状态
  • 死亡状态:自然执行完毕、外部干涉终止线程


具体流程为:



3.1、线程的常用方法

方法 说明
setPriority(int newPriority) 更改线程的优先级
static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休眠
void join() 等待该线程终止
static void yield() 暂停当前正在执行的线程对象,并执行其他线程
void interrupt() 中断线程,别用这个方式
boolean isAlive() 测试线程是否处于活动状态

3.2、停止线程

  • 不推荐使用JDK提供的stop(); destroy();方法【已弃用】
  • 推荐线程自己停止
  • 建议使用一个标志位进行终止变量
public class TestStop implements Runnable {
    // 测试stop
	//1.建议线程正常停止--->利用次数,不建议死循环。建议使用标志位--->设置一个标志位
	//3.不要使用stop或者destroy等过时或者JDK不建议使用的方法
    //1. 设置标志位
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while(flag){
            System.out.println("Thread is running...." + i++);
        }
    }

    // 2. 设置一个公开的方法停止线程,转换标志位

    public void stop(){
        this.flag = false;
    }

    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        // main 先执行
        for (int i = 0; i < 1000; i++) {
            System.out.println("main is running..." + i);
            if(i == 900){
                //调用stop方法停止线程
                testStop.stop();
                System.out.println("Thread stop!");
            }
        }
    }

}

总结一下:

public class TestStop implements Runnable {
    //1.线程中定义线程体使用的标识
     private boolean flag = true;
    
     @Override
    public void run() {
        //线程体中使用该标识
        int i = 0;
        while(flag){
            System.out.println("......");
        }
    }
}

//对外提供方法改变标识
    public void stop(){
        this.flag = false;
    }

3.3、线程休眠——sleep()

  • sleep(时间)指定当前线程阻塞的毫秒数
  • sleep 存在异常 InterruptedException
  • sleep 时间达到后线程进入就绪状态
  • sleep 可以模拟网络延时,倒计时等 (可以故意设置延时收优化钱,不建议这样做,不道德)
  • sleep 每一个对象都有一个锁,sleep 不会释放锁

使用案例:

package com.thread.sleep;

//模拟延时:放大问题的发生性
import java.text.SimpleDateFormat;
import java.util.Date;

public class TestSleep {
    public static void main(String[] args) throws InterruptedException {
        //  down();
        // 打印当前系统时间
        Date date = new Date(System.currentTimeMillis());
        while (true){
            try {
                Thread.sleep(1000);
                //SimpleDateFormat("HH:mm:ss")是时间格式化工厂
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));
                date = new Date(System.currentTimeMillis()); // 更新当前时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //public static void down() throws InterruptedException {
        //int num = 10;
        //while (true){
            //Thread.sleep(1000);
            //System.out.println(num --);
            //if(num <= 0) break;
        //}
    //}
}

3.4、线程礼让——yield()

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让 CPU 重新调度,有可能还是调度该礼让线程

注意:礼让不一定成功!

案例:

package com.thread.yield;

public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"a").start();
        new Thread(myYield,"b").start();
    }
}



class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

结果1(礼让成功):

结果2(礼让不成功):

img


3.5、合并线程——Join()

  • join合并线程,待此线程执行完成后再执行其他线程,其他线程阻塞
  • 可以想象成插队
package com.thread.Join;

public class TestJoin implements Runnable{

    public static void main(String[] args) throws InterruptedException {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();
        for (int i = 0; i < 300; i++) {
            if (i == 200){
                thread.join();
            }
            System.out.println("普通线程"+i);
        }

    }


    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("vip线程来了!"+i);
        }
    }
}

3.6、观测线程状态

  • Thread.State

线程状态:线程可以处于以下状态之一:

  • NEW:尚未启动的线程处于此状态。
  • RUNNABLE:在 Java 虚拟机中执行的线程处于此状态。
  • BLOCKED:被阻塞等待监视器锁定的线程处于此状态。
  • WAITING:正在等待另一个线程执行特定动作的线程处于此状态。
  • TERMINATED:已退出的线程处于此状态。

注意点:

  1. 一个线程可以在给定时间点处于一个状态。 这些状态是不反映任何操作系统线程状态的虚拟机状态。
  2. 一个线程只能启动一次,终止之后就不能再start了

案例:

package com.thread.state;

//观测测试线程状态
public class TestState {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("/////");
        });


        ///观察状态
        Thread.State state = thread.getState();
        System.out.println(state); //new

        //观察启动后
        thread.start();
        state = thread.getState();//run

        //观测死亡
        while(state != Thread.State.TERMINATED){  //只要线程不终止就一直输出状态
            Thread.sleep(100);//运行时是在阻塞状态中
            state = thread.getState();//更新线程状态
            System.out.println(state);//输出状态
        }
    }
}

3.7、线程优先级

优先级:priority

Java 提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。

  • 线程的优先级用数字表示,范围从1~10
    • Thread.MIN_PRIORITY=1
    • Thread.MAX_PRIORITY=10
    • Thread.NORM_PRIORITY=5
  • 使用以下方式改变或获取优先级
    • getPriority().setPriority(int xxx)

优先级的设定建议在 start() 调度前

案例:

package com.thread.priority;
//测试线程优先级
public class TestPriority {
    public static void main(String[] args) {
        //主线程优先级
        System.out.println(Thread.currentThread().getName() + "-->"
                + Thread.currentThread().getPriority());
        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);
        Thread t6 = new Thread(myPriority);

        //设置优先级
        t1.start();

        t2.setPriority(1);
        t2.start();

        t3.setPriority(4);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY);//最大优先级 10
        t4.start();

//        t5.setPriority(-1);
//        t5.start();
//
//        t6.setPriority(11);
//        t6.start();
    }
}


class MyPriority implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "-->" + Thread.currentThread().getPriority());

    }
}

优先级低只是意味着获得调度的概率低,并不是高优先级必然先调用 (性能倒置问题)


3.8、守护(daemon)线程

  • 线程分为用户线程守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如后台记录操作日志,监控内存、垃圾回收等待
package com.thread.daemon;
//测试守护线程
//上帝守护你
public class TestDaemon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        thread.setDaemon(true);//默认线程是用户线程
        thread.start();


        new Thread(you).start();//你启动

    }

}


//上帝
class God implements Runnable {

    @Override
    public void run() {
        while (true) {
            System.out.println("上帝永远保佑着你!");
        }
    }
}

//你
class You implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("你开心的生活每一天!");
        }

        System.out.println("=========goodbye,world!");
    }
}