多线程之线程实现和状态

多线程

1.多线程Thread概述

1.1线程简介

  • 多任务: 同时做多件事情(一遍看电视一遍吃饭);不过,看起来是多个任务同时在做,其实本质上我们的大脑在同一时间依旧是只做了一件事(速度太快了)

  • 多线程: 高速收费口,只有一个收费口,车太多了会导致排队堵塞,通过效率太低了;为了解决堵塞,又新开了多个收费口,可以同时缴费通过。

  • 进程(Process): 执行程序的一次执行过程,动态概念 包含若干个线程,至少一个。操作系统中运行的程序就是进程比如:QQ,播放器,游戏,IDE…;

  • 普通方法调用和多线程示例图

    • 执行过程中,调用主线程后,主线程调用start方法执行子线程,子线程执行run方法,主线程和子线程同时并行交替执行
      hhh
  • Process 进程 和 Thread 线程

    • 说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。

    • 而进程则是执行程序的依次执行过程,它是一个动态的概念。是系统资源分配的单位。

    • 通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位

    • 注意: 很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能执行一个代码,因为切换的很快,所以就有同时执行的错局。

  • 核心概念:

    • 线程就是独立的执行路径
    • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,比如主线程,GC线程
    • main()称之为主线程,为系统的入口,用于执行整个程序

    • 在一个进程中,如果开辟了多个线程,线程的运行是由调度器(cpu)安排调度的,调度器是与操作系统紧密相关的,先后顺序是不能人为干预的

    • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制

    • 线程会带来额外的开销,如CPU调度时间,并发控制开销

    • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

2. 线程实现

2.1 线程三种创建方式

  • 创建线程的三种方式:
    • (1)继承Thread类,重写run方法。
    • (2)实现Runnable接口。
    • (3)实现Callable接口
  • 三种创建方式
    iii

2.2 线程实现->继承Thread类

  • 自定义线程类继承Thread类
  • 重写run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动线程
  • 注意: 线程不一定立即执行,CPU安排调度(这个方式不建议使用,存在面向对象OOP单继承局限性)

2.21 继承Thread类创建线程代码演示

package threads.demo01;
//创建线程方式一:继承thread 类,重写run() 方法,调用start 开启线程
public class TestThread extends Thread{
    @Override
    public void run() {

        //run 方法线程体
        for (int i = 0; i < 10; i++) {
            System.out.println("我再测试run方法"+i);

        }

    }

    public static void main(String[] args) {
        //main 线程,主线程

        //创建一个线程对象
        TestThread testThread = new TestThread();
        //调用start()方法开启线程
        testThread.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("我在学习多线程"+i);
        }

    }
}


运行结果

我在学习多线程0
我再测试run方法0
我在学习多线程1
我再测试run方法1
我在学习多线程2
我再测试run方法2
我在学习多线程3
我在学习多线程4
我在学习多线程5
我在学习多线程6
我再测试run方法3
我再测试run方法4
我再测试run方法5
我再测试run方法6
我再测试run方法7
我再测试run方法8
我再测试run方法9
我在学习多线程7
我在学习多线程8
我在学习多线程9


2.22 实现多线程同步下载图片代码演示


package threads.demo01;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
//练习thread ,实现多线程同步下载图片

public class TestThread1 extends Thread{
    private String url;//网络图片地址
    private String name;//保存的文件名

    public TestThread1(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    //下载图片线程的执行体
    public void run() {//(2)使用多线程方法下载

        // 进入线程后,创建一个下载器,下载器通过 downloader 方法,传入 url 和 name 下载相应的资源
        WebDownloard webDownloard = new WebDownloard();
        webDownloard.dewnloard(url,name);
        System.out.println("下载的文件名:"+name);
    }

    public static void main(String[] args) {

        //(3)主方法内填入网址及名字,并运行
        TestThread1 testThread1 = new TestThread1("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG","test1.jpg");
        TestThread1 testThread2 = new TestThread1("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG","test2.jpg");
        TestThread1 testThread3 = new TestThread1("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG","test3.jpg");

             testThread1.start();
             testThread2.start();
             testThread3.start();

    }


}

//下载器(1)
class WebDownloard{
    //下载方法
    public void dewnloard(String url,String name)  {


        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO 异常,图片copy失败");
        }
    }

}


运行结果

下载的文件名:test3.jpg
下载的文件名:test2.jpg
下载的文件名:test1.jpg


2.3 线程实现->实现Runnable接口

  • 自定义线程类实现Runnable接口
  • 实现run()方法,编写线程执行体
  • 创建线程对象,调用start()方法启动对象
  • 推荐使用 Runnable,因为 Java 单继承的局限性,方便 一个对象被多个线程使用

  • Thread和Runnable对比

  • 继承Thred类:

    • 子类继承Thread类具备多线程能力
    • 启动线程:子类对象.start()
    • 不建议使用:避免OOP单继承局限性
  • 实现Runnable接口:

    • 实现接口Runnable具有多线程能力
    • 启动线程:传入目标对象+Thread对象.start()
    • 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用

2.31 实现Runnable接口接口创建线程代码演示

package threads.demo01;
//实现Runnable:实现Runnable接口,重写run方法,执行线程需要丢入Runnable接口实现类,调用start方法
public class RunnableTest implements Runnable{
    @Override
    public void run() {
        //run方法线程体
        for (int i = 0; i < 10; i++) {
            System.out.println("线程的run方法");
        }

    }


    public static void main(String[] args) {
        //main线程,主线程

        for (int i = 0; i < 10; i++) {
            System.out.println("我在学习多线程");
        }

        //创建一个线程对象
        RunnableTest runnableTest = new RunnableTest();
        //创建线程对象,通过线程对象来开启我们的多线程,代理
        // Thread thread = new Thread(runnableTest);
        // thread.start();


        //简写
        //调用start()开启线程
        new Thread(runnableTest).start();
    }
}


运行结果

我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
我在学习多线程
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法
线程的run方法



2.32 实现Runnable接口copy图片代码演示


package threads.demo01;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
//使用实现Runnable接口,重写run方法,copy图片
public class RunnableCopyTest implements Runnable{

    private String url;
    private String name;

    public RunnableCopyTest(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        DownloaderUrl downloaderUrl = new DownloaderUrl();
        downloaderUrl.download(url,name);
        System.out.println("文件名称为:"+name);

    }


    public static void main(String[] args) {
        RunnableCopyTest t1 = new RunnableCopyTest("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG", "amage1.jpg");
        RunnableCopyTest t2 = new RunnableCopyTest("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG", "amage2.jpg");
        RunnableCopyTest t3 = new RunnableCopyTest("https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG", "amage3.jpg");

        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();

    }

}

class DownloaderUrl{

    public void download(String url,String name){

        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,文件复制失败");
        }
    }
}


运行结果

文件名称为:amage3.jpg
文件名称为:amage2.jpg
文件名称为:amage1.jpg



2.33 并发问题买火车票案例代码演示


package threads.demo01;
//多线程同时操作同一个对象
//买火车票的例子

// 问题:产生了并发问题,多个线程同时对一个资源进行操作,线程不安全,发生了数据紊乱
public class TicketRunnableTest implements Runnable{

    // 票数
    private int ticketNumb=20;

    @Override
    public void run() {

        while (true){
            if(ticketNumb<=0){
                break;
            }
            // 模拟演示
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNumb--+"张火车票");
        }

    }

    public static void main(String[] args) {
        TicketRunnableTest t1 = new TicketRunnableTest();
        new Thread(t1,"张三").start();
        new Thread(t1,"李四").start();
        new Thread(t1,"黄牛党").start();

    }

}


运行结果

黄牛党抢到了第20张火车票
张三抢到了第19张火车票
李四抢到了第18张火车票
黄牛党抢到了第17张火车票
张三抢到了第16张火车票
李四抢到了第15张火车票
黄牛党抢到了第14张火车票
张三抢到了第13张火车票
李四抢到了第12张火车票
黄牛党抢到了第11张火车票
张三抢到了第10张火车票
李四抢到了第9张火车票
黄牛党抢到了第8张火车票
张三抢到了第7张火车票
李四抢到了第6张火车票
黄牛党抢到了第5张火车票
张三抢到了第4张火车票
李四抢到了第3张火车票
黄牛党抢到了第2张火车票
张三抢到了第1张火车票
李四抢到了第0张火车票
黄牛党抢到了第-1张火车票


2.34 模拟龟兔赛跑案例代码演示


/*
1.首先来个赛道距离,然后要离终点越来越近
2.判断比赛是否结束
3.打印出胜利者
4.龟兔赛跑开始
5.故事中是乌龟赢的,兔子需要睡觉,所以我们来模拟兔子睡觉。
6.终于,乌龟赢得比赛。
*/

/*
1.定义一个赢家
2.设定赛道
3.判断输赢
4.确定比赛是否结束

 */
package threads.demo01;
// 模拟龟兔赛跑
public class RaceTest implements Runnable{

    // 胜利者
    private static String winner;// static 保证在调用时只有一个 winner

    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {//赛道

             // 模拟兔子休息
            if(Thread.currentThread().getName().equals("兔子")&&i%10==0){

                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }


            // 判断比赛是否结束
            Boolean flag = gameOver(i);
            // 如果比赛结束 停止程序
            if (flag){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");

        }

    }
    //判断输赢
    //假如winner不为空就说明已经有冠军
    //假如steps>=100,则产生冠军
    //否则比赛还没结束

    //判断是否完成比赛
    public Boolean  gameOver(int step){
        // 判断是否有胜利者
        if (winner!=null){// 已经存在胜利者
            return true;
        }
        {//这里{}只是一个代码块,可以不写,直接if语句,方便理解。写了美观
            if (step >= 100) {
                winner = Thread.currentThread().getName();
                System.out.println("胜利者是:" + winner);
                return true;
            }
        }
        return false;

    }


    public static void main(String[] args) {

        RaceTest raceTest = new RaceTest();
        new Thread(raceTest,"兔子").start();
        new Thread(raceTest,"乌龟").start();
    }


}



运行结果

乌龟跑了0步
乌龟跑了1步
乌龟跑了2步
乌龟跑了3步
乌龟跑了4步
乌龟跑了5步
乌龟跑了6步
乌龟跑了7步
乌龟跑了8步
乌龟跑了9步
乌龟跑了10步
乌龟跑了11步
乌龟跑了12步
乌龟跑了13步
乌龟跑了14步
乌龟跑了15步
乌龟跑了16步
乌龟跑了17步
乌龟跑了18步
乌龟跑了19步
乌龟跑了20步
乌龟跑了21步
乌龟跑了22步
乌龟跑了23步
乌龟跑了24步
乌龟跑了25步
乌龟跑了26步
乌龟跑了27步
乌龟跑了28步
乌龟跑了29步
乌龟跑了30步
乌龟跑了31步
乌龟跑了32步
乌龟跑了33步
乌龟跑了34步
乌龟跑了35步
乌龟跑了36步
乌龟跑了37步
乌龟跑了38步
乌龟跑了39步
乌龟跑了40步
乌龟跑了41步
乌龟跑了42步
乌龟跑了43步
乌龟跑了44步
兔子跑了0步
乌龟跑了45步
兔子跑了1步
乌龟跑了46步
兔子跑了2步
兔子跑了3步
乌龟跑了47步
兔子跑了4步
乌龟跑了48步
兔子跑了5步
乌龟跑了49步
兔子跑了6步
乌龟跑了50步
兔子跑了7步
兔子跑了8步
乌龟跑了51步
兔子跑了9步
乌龟跑了52步
乌龟跑了53步
乌龟跑了54步
乌龟跑了55步
乌龟跑了56步
乌龟跑了57步
乌龟跑了58步
乌龟跑了59步
乌龟跑了60步
乌龟跑了61步
乌龟跑了62步
乌龟跑了63步
乌龟跑了64步
乌龟跑了65步
乌龟跑了66步
乌龟跑了67步
乌龟跑了68步
乌龟跑了69步
乌龟跑了70步
乌龟跑了71步
乌龟跑了72步
乌龟跑了73步
乌龟跑了74步
乌龟跑了75步
乌龟跑了76步
乌龟跑了77步
乌龟跑了78步
乌龟跑了79步
乌龟跑了80步
乌龟跑了81步
乌龟跑了82步
乌龟跑了83步
乌龟跑了84步
乌龟跑了85步
乌龟跑了86步
乌龟跑了87步
乌龟跑了88步
乌龟跑了89步
兔子跑了10步
乌龟跑了90步
兔子跑了11步
乌龟跑了91步
乌龟跑了92步
乌龟跑了93步
乌龟跑了94步
乌龟跑了95步
乌龟跑了96步
乌龟跑了97步
兔子跑了12步
乌龟跑了98步
兔子跑了13步
乌龟跑了99步
兔子跑了14步
胜利者是:乌龟



2.4 线程实现->实现Callable接口(了解即可)

  • 1.实现Callable接口,需要返回值类型
  • 2.重写call方法,需要抛出异常
  • 3.创建目标对象
  • 4.创建执行服务: ExecutorService ser = Executors.newFixedThreadPool(1);
  • 5.提交执行: Future< Boolean > result1 = ser.submit(t1);
  • 6.获取结果: boolean r1 = result1.get();
  • 7.关闭服务: ser.shutdownNow();

2.41 利用callable 改造下载图片案例代码演示

package threads.demo01;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//线程创建方式三:实现calladle接口
//重写实现的是call方法,并需要设置返回Boolean值(也可以设置其他类型返回值)
/*
callable的好处
  1.可以定义返回值
  2.可以抛出异常
*/
/*
    1. 实现Callable接口
    2.实现call方法
    3.创建执行服务
    4.提交执行
    5.获取结果
    6.关闭服

 */
//利用callable 改造下载图片案例
public class TestCallable implements Callable<Boolean> {

    private  String url;//网络图片地址
    private  String name;//保存的文件名

    public TestCallable(String name,String url) {
        this.name = name;
        this.url=url;
    }

    //下载图片线程的执行体
    @Override
    public Boolean call() {
        WebDownLoader1 webDownLoader1 = new WebDownLoader1();
        webDownLoader1.DownLoader(url,name);
        System.out.println("下载了文件为:"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1 = new TestCallable("1.jpg","https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG");
        TestCallable t2 = new TestCallable("2.jpg","https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG");
        TestCallable t3 = new TestCallable("3.jpg","https://img6.bdstatic.com/img/image/pcindex/sunjunpchuazhoutu.JPG");

        //4.创建执行服务
        ExecutorService ser= Executors.newFixedThreadPool(3);

        //5.提交执行
        Future<Boolean> result1=ser.submit(t1);
        Future<Boolean> result2=ser.submit(t2);
        Future<Boolean> result3=ser.submit(t3);

        //6.获取结果
        Boolean r1 =result1.get();
        Boolean r2 =result1.get();
        Boolean r3 =result1.get();
        System.out.println(r1);
        System.out.println(r2);
        System.out.println(r3);

        //7.关闭服务:
        ser.shutdownNow();
    }

}
class WebDownLoader1 {
    //下载方法
    public void DownLoader(String url,String name) {
        try {
            FileUtils.copyURLToFile(new URL(url), new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常 download方法出现问题");
        }
    }
}


运行结果

下载了文件为:2.jpg
下载了文件为:3.jpg
下载了文件为:1.jpg
true
true
true

2.5 静态代理

  • 你:真实角色
  • 婚庆公司:代理你,帮你处理结婚的事
  • 都实现结婚接口即可(真实对象和代理对象都要实现同一个接口)
  • 好处:代理对象可以做真实对象做不了的事情;真实对象只需要做好自己的事情就好。
  • 多线程原理与静态代理模式很像-->Thread和WeddingCompany都是代理,都代理了真实对象Runnable和Marry

2.51 实现静态代理对比Thread代码演示

package threads.demo01;
//静态代理模式总结
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色,做了“结婚前”和“结婚后”的事,而真实对象只需要“结婚”

/*
    好处:
        1. 代理对象可以做很多真实对象做不了的事情
        2. 真实对象专注做自己的事情
 */

//  Runnable   对比
       /*
                  //创建一个线程对象
               RunnableTest runnableTest = new RunnableTest();
                //创建线程对象,通过线程对象来开启我们的多线程,代理
                // Thread thread = new Thread(runnableTest);
                // thread.start();

                //简写  调用start()开启线程
                new Thread(runnableTest).start();

               Runnable接口只有抽象run()方法:public abstract void run();
               Thread相对于婚庆公司,也实现了Runnable接口:  class Thread implements Runnable
               并且Thread构造方法需要传入实现Runnable接口的对象:public Thread(Runnable target)
               Thread的括号里是真正的线程对象


                    new  Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("我爱你");
                    }
                    }).start();

         Lambda表达式:  new  Thread(()->System.out.println("我爱你")).start();



*/
//  实现静态代理对比Thread演示

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

        you you = new you();//你要结婚
        you.HappyMarry();
        System.out.println("使用静态代理==========");
        weddingCompany weddingCompany = new weddingCompany(new you());//真实要结婚的目标
        weddingCompany.HappyMarry();

        /*
        多线程原理与静态代理模式很像
        Thread和WeddingCompany都是代理,都代理了真实对象
         */
    }

}

interface Marry{
    void HappyMarry();
}
// 实际需要结婚的角色 You
class you implements Marry{

    @Override
    public void HappyMarry() {
        System.out.println("我要结婚了,超开心");
    }
}
//代理角色,帮助你结婚
class weddingCompany implements Marry{

    private Marry tager;//创建目标对象

    public weddingCompany(Marry tager) {
        this.tager=tager;
    }


    @Override
    public void HappyMarry() {
        befor();
        this.tager.HappyMarry(); //目标对象进行结婚
        after();
    }


    private void befor() {
        System.out.println("结婚之前,布置现场");
    }
    private void after() {
        System.out.println("结婚之后,收尾款");
    }
}

运行结果

我要结婚了,超开心
使用静态代理==========
结婚之前,布置现场
我要结婚了,超开心
结婚之后,收尾款


2.6 Lamda表达式

  • λ 希腊字母表中排序第十一位的字母,英语名称为 Lamda
  • 避免匿名内部类定义过多
  • 其实质属于函数式编程的概念
  • 可以让你的代码看起来很简洁,去掉了一堆没有意义的代码,只留下核心逻辑

  • 函数式接口的定义:
    • 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
    • 对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
public interface Runnable{
       public abstract void run();
}

2.61 推导lamda表达式代码演示

package threads.demo01;
/**
 * 推导lamda表达式
 */
public class TestLambda {
    public static void main(String[] args) {

        Like like = new Like();
        like.lambda();

        Like1 like1 = new Like1();
        like1.lambda();


        // //4.局部内部类
        class Like2 implements ILike{

            @Override
            public void lambda() {
                System.out.println("我是局部内部类");
            }
        }
        Like2 like2 = new Like2();
        like2.lambda();


        // //5.匿名内部类,没有类的名称,必须借助接口或者父类
        ILike like3 = new ILike() {
            @Override
            public void lambda() {
                System.out.println("我是匿名内部类");
            }
        };


        /*
        //6.lamda简化
        like3 = ()-> {
            System.out.println("I like lambda5");
        };
        like3.lambda();
*/

    }

    //3. 静态内部类
    static class Like1 implements ILike{

        @Override
        public void lambda() {
            System.out.println("我是静态内部类");
        }
    }



}

// 1.定义一个函数式接口
interface ILike{
    void lambda();
};

// 2.实现类
class Like implements ILike{
    @Override
    public void lambda() {
        System.out.println("我是外部类");
    }
}

运行结果

我是外部类
我是静态内部类
我是局部内部类
我是匿名内部类


2.62 lamda表达式简化代码演示

public class Demo14_LamdaCase2 {
    public static void main(String[] args) {
        // 1.lamda
        ILove love = (int a) -> {
            System.out.println("I love you -->" + a);
        };
        // 2.lamda简化1.0
        love = (a) -> {
            System.out.println("I love you -->" + a);
        };
        // 3.lamda简化2.0
        love = a -> {
            System.out.println("I love you -->" + a);
        };
        // 3.lamda简化3.0
        love = a -> System.out.println("I love you -->" + a);

        /**总结:
         * Lambda表达式只能有一行代码(函数式接口)的情况下才能简化成为一条,如果有多行,那么就用代码块包裹{}
         * 前提是接口为函数式接口(只能有一个方法)
         * 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上()
         */

        love.love(520);
    }
}

interface ILove {
    void love(int a);
}



3.线程状态

3.1 线程五大状态:

  • 创建状态:new 一个线程对象,一旦创建就进入到了新生状态;

  • 就绪状态:调用start方法,线程立即进入就绪状态,并非马上开始执行,等待Cpu调度执行;

  • 运行状态:Cpu心情好调度该线程,进入运行状态,线程开始执行代码块;

  • 阻塞状态:当用户调用了sleep、wait、或同步锁定等方法时,线程进入阻塞状态,就是代码不继续执行,等待阻塞解除,解除后会重新进入就绪状态,等待Cpu再次调度;

  • 死亡状态:线程中断或结束,一旦进入死亡状态,就不能再次启动;


jj

kk

3.2 线程方法

  • setPriority(int newPriority) 更改线程的优先级
  • static void sleep(long millis) 在指定的毫秒数内让当前正在执行的线程休息(会抛出异常)
  • void join() 等待该线程终止(超车)
  • static vioid yield() 暂停当前正在执行的线程对象,并执行其他线程
  • void intreeupt() 中断线程(建议不要用这个方法)
  • boolean isAlive() 测试线程是否处于活动状态

3.21 线程停止

  • 特点:
    • 不推荐使用JDK提供的stop()、destroy()方法【已废弃】

    • 推荐线程自己停下来

    • 建议使用一个标志位来终止变量 当flag=false ,则终止线程运行

3.21 使用标志位线程停止代码演示:

package threads.demo02;
/*
    测试stop
    1. 建议线程正常停止-->利用次数,不建议死循环(死循环也要限制运行速度,防止cpu卡死)
    2.建议使用标志位-->设置一个标志位
    3.不要使用 stop 或者 destroy 等过时或者JDk不建议使用的方法
 */
//测试stop
public class TestStop implements Runnable{

    //1.设置一个标志位
    private boolean flag=true;
    //多线程方法
    @Override
    public void run() {
        int i=0;
        while (flag) {
                System.out.println("run...Thread" + i++);
        }

    }


    //2.设置一个公开的方法停止线程,转换标志位
    public void stop(){
        this.flag=false;
    }

    public static void main(String[] args) {
        //开启多线程
        TestStop testStop = new TestStop();
        new Thread(testStop).start();

        for (int i = 0; i < 30; i++) {
            System.out.println("主线程在运行"+i);

            if(i==20){
                //3.调用stop方法切换标志位,让线程停止
                testStop.stop();
                System.out.println("线程停止了");
            }

        }
    }
}

运行结果

主线程在运行0
run...Thread0
主线程在运行1
run...Thread1
主线程在运行2
主线程在运行3
主线程在运行4
主线程在运行5
主线程在运行6
主线程在运行7
主线程在运行8
主线程在运行9
主线程在运行10
主线程在运行11
主线程在运行12
主线程在运行13
主线程在运行14
主线程在运行15
主线程在运行16
主线程在运行17
主线程在运行18
run...Thread2
run...Thread3
run...Thread4
run...Thread5
run...Thread6
run...Thread7
run...Thread8
run...Thread9
run...Thread10
run...Thread11
run...Thread12
run...Thread13
run...Thread14
run...Thread15
run...Thread16
run...Thread17
run...Thread18
run...Thread19
run...Thread20
run...Thread21
run...Thread22
run...Thread23
run...Thread24
run...Thread25
run...Thread26
run...Thread27
run...Thread28
主线程在运行19
主线程在运行20
run...Thread29
线程停止了
主线程在运行21
主线程在运行22
主线程在运行23
主线程在运行24
主线程在运行25
主线程在运行26
主线程在运行27
主线程在运行28
主线程在运行29



3.22 线程休眠

  • 特点:
    • sleep(时间)指出当前线程阻塞的毫秒数

    • sleep存在异常InterruptedException

    • sleep时间达到后线程进入就绪状态

    • sleep可以模拟网络延迟,倒计时等

    • 每一个对象都有一个锁,sleep不会释放锁

3.22 模拟网络延迟代码演示:

package threads.demo02;

import threads.demo01.TicketRunnableTest;
//模拟网络延迟,放大问题的发生性
public class TestSleep1 implements Runnable{

    // 票数
    private int ticketNumb=10;

    @Override
    public void run() {

        while (true){
            if(ticketNumb<=0){
                break;
            }
            // 模拟演示
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"抢到了第"+ticketNumb--+"张火车票");
        }

    }

    public static void main(String[] args) {

        TestSleep1 t1 = new TestSleep1();
        new Thread(t1,"张三").start();
        new Thread(t1,"李四").start();
        new Thread(t1,"黄牛党").start();

    }
}


运行结果

黄牛党抢到了第10张火车票
张三抢到了第9张火车票
李四抢到了第8张火车票
李四抢到了第6张火车票
张三抢到了第5张火车票
黄牛党抢到了第7张火车票
黄牛党抢到了第4张火车票
张三抢到了第3张火车票
李四抢到了第2张火车票
李四抢到了第1张火车票
张三抢到了第0张火车票
黄牛党抢到了第-1张火车票

3.22 模拟倒计时代码演示:

package threads.demo02;

import java.text.SimpleDateFormat;
import java.util.Date;

//模拟倒计时
public class TestSleep2 {

    public static void main(String[] args) {

        try {
            TestSleep2.tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        //打印当前系统时间
        Date date = new Date(System.currentTimeMillis()); //获取系统当前时间

        while (true){
            try {
                Thread.sleep(1000);
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(date));//打印时间格式
                date = new Date(System.currentTimeMillis()); //更新系统当前时间

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


    }

    //模拟倒计时
    public static void tenDown() throws InterruptedException {

        int numb=10;
        while (true){
            Thread.sleep(1000);
            System.out.println(numb--);

            if(numb<=0) {
                break;
            }
        }

    }

}



运行结果

10
9
8
7
6
5
4
3
2
1
16:54:26
16:54:27

3.23 线程礼让_yield

  • 特点:
    • 礼让线程,就是让当前正在运行的线程暂停,但不阻塞

    • 将线程从运行状态转为就绪状态

    • 礼让就是让 CPU 重新调度,所以礼让不一定成功

3.23 测试礼让线程代码演示:

package threads.demo02;

/**
 * 测试礼让线程
 * 礼让不一定成功,看cpu心情
 */
//测试礼让线程

public class TestYeild {

    public static void main(String[] args) {
        MyYeild testYeild = new MyYeild();
        new Thread(testYeild, "a").start();
        new Thread(testYeild, "b").start();
    }



}
class MyYeild implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "开始运行");

        Thread.yield();//礼让
        System.out.println(Thread.currentThread().getName() + "运行停止");
    }
}

运行结果

a开始运行
b开始运行
b运行停止
a运行停止

3.25 线程强制执行_join

  • 特点:
    • Join 合并线程,待此线程执行完成后,再执行其他线程。此时其他线程阻塞

    • 可以想象成生活中插队

3.25 测试join代码演示:

package threads.demo02;
/**
 * 测试join
 * 插队
 */
public class TestJoin implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("VIP来插队了"+i);
        }
    }


    public static void main(String[] args) throws InterruptedException {
        //启动我们的线程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);

        //主线程
        for (int i = 0; i < 10; i++) {

            if(i==5){
                thread.start();
                thread.join();//插队
            }
            System.out.println("main线程"+i);
        }

    }

}

运行结果

main线程0
main线程1
main线程2
main线程3
main线程4
VIP来插队了0
VIP来插队了1
VIP来插队了2
VIP来插队了3
VIP来插队了4
VIP来插队了5
VIP来插队了6
VIP来插队了7
VIP来插队了8
VIP来插队了9
VIP来插队了10
VIP来插队了11
VIP来插队了12
VIP来插队了13
VIP来插队了14
VIP来插队了15
VIP来插队了16
VIP来插队了17
VIP来插队了18
VIP来插队了19
main线程5
main线程6
main线程7
main线程8
main线程9


3.26 线程状态观测

  • Thread.State
  • 线程状态。线程可以处于以下状态之一:
    • NEW 尚未启动的线程处于此状态

    • RUNNABLE 在Java虚拟机中执行的线程处于此状态

    • BLOCKED 被阻塞等待监视器锁定的线程处于此状态

    • WAITING 在等待另一个线程执行特定动作的线程处于此状态

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

    • TERMINATED 已退出的线程处于此状态(死亡之后的线程就不能再启动了)

  • 一个线程可以在给定时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态。

3.26 观察测试线程状态代码演示:

package threads.demo02;
//观察测试线程的状态
public class TestState {

    public static void main(String[] args) throws InterruptedException {

        final Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                System.out.println("//////////////");
                
            }
        });

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


        //观察启动后
        thread.start();//启动线程
        state=thread.getState();
        System.out.println(state);//Run

        while (state!=Thread.State.TERMINATED){//只要现成不终止,就一直输出状态
            Thread.sleep(100);
            state=thread.getState();//更新线程状态
            System.out.println(state);//输出状态

        }

        //死亡后的线程不能再启动了,启动会报异常
        //thread.start();
    }
    
}

运行结果

NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
RUNNABLE
RUNNABLE
//////////////
TERMINATED


3.27 线程优先级【Priority】

  • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
  • 线程的优先级用数字表示,范围从1~10

    • Thread.MIN_PRIORITY = 1;
    • Thread.MAX_PRIORITY = 10;
    • Thread.NORM_PRIORITY = 5;
  • 使用以下方式改变或获取优先级

    • getPriority()&&setPriority(int xxx)
  • 优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用,这都是看CPU的调度(性能倒置)

  • 注意:建议在调用start()前设置优先级

3.27 测试线程优先级代码演示:

package threads.demo02;
//测试线程的优先级
//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);

        t1.start(); //子程序默认优先级

        //先设置优先级,再设置启动
        t2.setPriority(6);
        t2.start();

        t3.setPriority(7);
        t3.start();

        t4.setPriority(Thread.MAX_PRIORITY);//MAX_PRIORITY最高级10
        t4.start();

        t5.setPriority(3);
        t5.start();

        /*t5.setPriority(-1);//低于边界1 抛出异常
        t5.start();

        t6.setPriority(11);//高于边界10 抛出异常
        t6.start();*/


    }
}


class MyPriority implements Runnable{

    //子线程优先级打印
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"===>"+Thread.currentThread().getPriority());
    }
}

运行结果

main===>5
Thread-0===>5
Thread-2===>7
Thread-3===>10
Thread-1===>6
Thread-4===>3


3.28 守护线程【Daemon】

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕

  • 虚拟机不用等待守护线程执行完毕

  • 如,后代记录操作日志,监控内存,垃圾回收等待…

3.28 测试守护线程代码演示:

package threads.demo02;
//测试守护线程
//你要保护你爱的人
public class TestDaemon {
    public static void main(String[] args) {

        You you = new You();
        Lover lover = new Lover();

        Thread thread = new Thread(you);
        thread.setDaemon(true);//默认是false表示是用户线程,正常的线程都是用户线程

        thread.start();//你   守护线程启动
        new Thread(lover).start();//爱人    用户线程启用


    }
}

//你
class You implements Runnable{

    @Override
    public void run() {

        while (true){
            System.out.println("你守护着你爱的人");
        }

    }
}
//爱的人
class Lover implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("人生一百年");
        }
        System.out.println("======goodbye======");
    }
}


更多参考

狂神说java-多线程详解

posted @ 2022-08-30 19:52  哼哼哈¥  阅读(44)  评论(0编辑  收藏  举报