Day08_Java的多线程

1.程序、进程、线程

在这里插入图片描述

在这里插入图片描述

2. Java线程的创建(重点)

在这里插入图片描述

2.1 线程Thread类的使用

在这里插入图片描述

//继承Thread类,重写run方法,调用start开启线程
public class TestThread1 extends Thread {

    @Override//重写父类的Thread的run方法
    public void run() {
        //run方法线程体
        for (int i = 0; i < 20; i++) {
            System.out.println("我在看代码==:"+i);
        }
    }

    public static void main(String[] args) {//main线程,主线程
        TestThread1 testThread1 = new TestThread1();//创建一个线程对象
        testThread1.start();//调用start方法,开启线程。与主线程同时执行,是交替执行
        //testThread1.run();//run方法。没有启动多线程,run跟普通方法一样,先运行run在接下来运行
        
        for (int i = 0; i < 20; i++) {
            System.out.println("我在学习多线程main:"+i);
        }
    }
}
  • 案例:Thread练习,实现多线程下载网图
//Thread练习,实现多线程下载网图
public class TestThread1_test extends Thread{
    private String url;//网络图片地址
    private String name;//保存的文件名

    public TestThread1_test(String url , String name)//创建构造器,初始化数值
    {
        this.url = url;
        this.name=name;
    }


    @Override//多线程重写run方法
    public void run() 
    {
        WebDoenloader webDoenloader = new WebDoenloader();
        webDoenloader.downloader(url,name);
        System.out.println("下载的文件名为:"+ name);
    }


    public static void main(String[] args)
    {//main主线程
        TestThread1_test t1 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" 		, "1.jpg");
        TestThread1_test t2 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" 		, "2.jpg");
        TestThread1_test t3 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" 		, "3.jpg");

        t1.start();
        t2.start();
        t3.start();
    }
}


//下载器,定义一个下载器的类。  一个文件中只能有一个public class的类
class WebDoenloader
{
    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异常,downloader方法出现问题");
        }
    }
}

2.2 线程 Runnable接口(重点)

在这里插入图片描述

2.2.1runnable接口的基本使用 + 下载图片案例

r u n n a b l e 接口的使用 \textcolor{red}{runnable接口的使用} runnable接口的使用

package com.Thread.demo01;
//Runnable接口实现线程,重写run方法,创建一个Thread线程对象,将Runnable接口的实现对象丢入Thread对象,调用Thread的start
//推荐使用Runnable接口,因为Thread就是对了Runnale接口的实现
public class TestThread2 implements Runnable{//接入Runnable接口
    @Override//重写run方法
    public void run() {

        for (int i = 0; i < 20; i++) {
            System.out.println("run重写方法!"+i);
        }
    }

    public static void main(String[] args) {
        //创建一个TestThread2的runnable接口实例化对象
        TestThread2 testThread2 = new TestThread2();
        //创建一个Thread对象,通过hread对象对象来开启我们的线程,代理
        Thread thread = new Thread(testThread2);//可以把runnable丢进Thread()内,也就是丢进构造器内
        thread.start();//开启线程
        
        //可以把上面两行代码简写为:new Thread(testThread2).start();//这种就是静态代理

        for (int i = 0; i < 20; i++) {
            System.out.println("main方法"+i);
        }
    }
}
  • 案例: r u n n a b l e 接口实现图片下载 \textcolor{red}{案例:runnable接口实现图片下载} 案例:runnable接口实现图片下载
package com.Thread.demo01;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

//用runnable练习,实现多线程下载网图
public class TestThread2_test implements Runnable{
    private String url;//网络图片地址
    private String name;//保存的文件名

    public TestThread2_test(String url , String name)//创建构造器
    {
        this.url = url;
        this.name=name;
    }


    @Override//多线程重写run方法
    public void run() {
        WebDoenloader2 webDoenloader = new WebDoenloader2();
        webDoenloader.downloader(url,name);
        System.out.println("下载的文件名为:"+ name);
    }


    public static void main(String[] args) {//main主线程
        TestThread1_test t1 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" , "1.jpg");
        TestThread1_test t2 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" , "2.jpg");
        TestThread1_test t3 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" , "3.jpg");
		
        //runnable的启动
        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();
    }
}


//下载器,定义一个下载器的类。  一个文件中只能有一个public class的类
class WebDoenloader2
{
    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异常,downloader方法出现问题");
        }
    }
}


2.2.2 Runnable小节 + Runnable同一个对象被多个线程使用(重要)

在这里插入图片描述

  • 实例:买火车票的例子,多个线程同时操作同一个对象 \textcolor{red}{实例:买火车票的例子,多个线程同时操作同一个对象} 实例:买火车票的例子,多个线程同时操作同一个对象
//多个线程同时操作同一个对象
//买火车票的例子
//延时可以不加。这边加了延时发现问题,多个线程操控同一资源条件下,线程不安全,数据紊乱
public class TestThread_test2 implements Runnable{
    private int ticketNum=10;//火车的票数
    @Override//重写线程
    public void run() {
        while (true)
        {
            if(ticketNum<=0)
            {
                break;
            }
            /*
            try {
                Thread.sleep(200);//线程的延时 200毫秒
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            */
            
            System.out.println(Thread.currentThread().getName()+"-->拿到了第:"+ ticketNum +" 张票");
            //Thread.currentThread().getName()获得当前执行线程的名字,下面有给起名
            ticketNum=ticketNum-1;
        }
    }
    
    public static void main(String[] args) {
        TestThread_test2 t = new TestThread_test2();//只需要new一个。对象创建一个TestThread2的runnable接口实例化对象
        new Thread(t,"小明").start();//Thread线程可以起名字,就是线程的名字
        new Thread(t,"小王").start();
        new Thread(t,"小红").start();
    }
}

实例:龟兔赛跑 \textcolor{red}{实例:龟兔赛跑} 实例:龟兔赛跑

龟兔赛跑题目
//runnable案例龟兔赛跑
//注意:当其中一方到达20时候,会把名字赋给winner, winner是一个类变量。两者都受到同一个winner的值影响
public class TestThread_test3 implements Runnable{

    private static String winner;
    @Override
    public void run() {
        for (int i = 1; i <= 20; i++) {

            //模拟兔子睡觉
            if (Thread.currentThread().getName().equals("兔子")) ;
            {
                try {
                    Thread.sleep(15);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            //如果比赛结束了 就停止程序
            boolean flag = gameover(i);
            if(flag){
                System.out.println(flag+Thread.currentThread().getName()+"break");
                break;
            }
            System.out.println(Thread.currentThread().getName() + " 跑了"+ i +"米");
        }
    }

    //判断比赛是否结束
    public boolean gameover(int steps)
    {
        if (winner != null)//存在是否有胜利者
        {
            return true;
        }
        if (steps==20)
        {
            winner = Thread.currentThread().getName();
            System.out.println("winner is " +winner+ steps);
            return true;
        }
    return false;
    }

    public static void main(String[] args) {

        TestThread_test3 t = new TestThread_test3();
        new Thread(t,"乌龟").start();
        new Thread(t,"兔子").start();
    }
}

2.3 Callable接口(了解即可)

在这里插入图片描述

  • C a l l a b l e 接口用于实现图片的下载 \textcolor{red}{Callable接口用于实现图片的下载} Callable接口用于实现图片的下载
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

//实现callable接口
public class TestThread3 implements Callable<Boolean> {//1.实现callable接口。Callable接口需要一个返回值

    private String url;
    private String name;

    public TestThread3(String url, String name)//定义构造器,初始化值
    {
        this.name = name;
        this.url = url;
    }

    @Override//2.重写call方法
    public Boolean call(){
        WebDownload webDoenloader = new WebDownload();
        webDoenloader.download(url,name);
        System.out.println("下载的文件名为:"+ name);
        return true;
    }

	//main主线程运行
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestThread3 t1 = new TestThread3("https://www.kuangstudy.com/assert/course/c1/13.jpg" ,
                            "1.jpg");//太长了。 3. 创建目标对象
        TestThread3 t2 = new TestThread3("https://www.kuangstudy.com/assert/course/c1/13.jpg" ,
                "2.jpg");//太长了。 3. 创建目标对象
        TestThread3 t3 = new TestThread3("https://www.kuangstudy.com/assert/course/c1/13.jpg" ,
                "3.jpg");//太长了。 3. 创建目标对象

        ExecutorService ser = Executors.newFixedThreadPool(3);//4.创建执行服务,有几个线程丢几个
        Future<Boolean> result1 = ser.submit(t1);//5.提交执行(有几个线程提交几个)
        Future<Boolean> result2 = ser.submit(t2);//提交执行
        Future<Boolean> result3 = ser.submit(t3);//提交执行
        boolean r1 = result1.get();//获取结果,直接alt+回车抛出异常
        boolean r2 = result2.get();
        boolean r3 = result3.get();

        System.out.println(r1);
        System.out.println(r2);
        System.out.println(r3);
        ser.shutdownNow();//6.关闭服务
    }
}
//下载器
class WebDownload{

    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异常");
        }
    }
}

3. Lamda表达式

3.1 Lamda表达式的基本知识

  • Lamda表达式实质:属于函数式编程

  • Lamda表达式意义:1.避免匿名内部类定义过多

2.可以使代码变得更加的简洁

3.去掉了一些没有意义的代码,只留下了核心的逻辑

例子       I_like like6 = ()-> {
            System.out.println("匿名内部类I like lambda 6");
        };

3.2 Lanbda的基本使用(从静态内部类,局部内部类,匿名内部类中一步一步简化到Lanbda中使用)

  • Lamda表达式学习的关键: F u n c t i o n a l I n t e r f a c e ————函数式接口 \textcolor{red}{Functional Interface————函数式接口} FunctionalInterface————函数式接口
  • 函数式接口定义: 一个接口中只有一个抽象方法,就是一个函数式接口 \textcolor{red}{一个接口中只有一个抽象方法,就是一个函数式接口} 一个接口中只有一个抽象方法,就是一个函数式接口

对于函数式接口,我们可以通过lambda表达式来创建该接口的对象

package com.Thread.Lambda;
//一步一步简化从2.放在外部之中,到3.静态内部类中,到4.创建一个局部内部类中, 到5.匿名内部类 最后到Lanbda表达式

//lambda表达式只用函数式接口,一个接口中只有一个抽象方法,就是一个函数式接口
//我们只用一次,可以就放在同一页中,不用分开再重新定义。

//1.定义一个函数式接口
interface I_like{//在同一页中不用加public
    void lamda();
}

//2.实现类,来实现接口
class Like2 implements I_like{//在同一页中不用加public
    @Override
    public void lamda() {
        System.out.println("外部类I like lambda2 ");
    }
}

public class TestLambda1 {
    //3.静态内部法
    //使用静态内部法,将实现类放入静态内部类中。静态内部类需要在类前面加static
    static class Like3 implements I_like{//在同一页中不用加public

        @Override
        public void lamda() {
            System.out.println("静态内部类 I like lambda 3");
        }
    }

    public static void main(String[] args) {

        //4.局部内部类
        class Like4 implements I_like{//局部内部类。将接口的实现类放在psvm之中,或者放在main方法中。
            @Override
            public void lamda() {
                System.out.println("局部内部类I like lambda 4");
            }
        }


        I_like like2 = new Like2();//创建一个接口对象.接口的引用指向实现类的引用。实现类在外部
        like2.lamda();

        I_like like3 = new Like3();//静态内部类创建对象。一个接口对象,但是这个接口实现类在内部
        like3.lamda();

        I_like like4 = new Like4();//局部内部类。
        like4.lamda();

        //5.匿名内部类,没有类的名称,必须借助接口或者父类
        I_like like5 = new I_like(){//直接展开重写的方法
            @Override
            public void lamda() {
                System.out.println("匿名内部类I like lambda 5");
            }
        };
        like5.lamda();

        //6.用lanbdas表达式简化,在匿名局部变量上进一步简化。接口的引用指向实现类的引用
        //lambda表达式只用函数式接口,一个接口中只有一个抽象方法,就是一个函数式接口
        I_like like6 = ()-> {
            //去掉了:“直接展开重写的方法 new I_like() { @Override  public void lambda”
            //也就是去掉到方法括号之前
            System.out.println("匿名内部类I like lambda 6");
        };
        like6.lamda();
        
    }
}

//这上面只适合写一行重写方法,想要多可以这样写(Thread也是重写Runnable接口的) //5.6线程监控状态的补充
    Thread thread = new Thread(()->{//lambda方法,不用另外定义run重写方法
        for (int i = 0; i < 5; i++) {
            try {
                Thread.sleep(1_000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        }
    });

Lamvda表达式的进一步简化(这个例子是带有参数的)

package com.Thread.Lambda;
//Lambda的另外一个例子,带有参数的

interface Ilove{//1.定义接口
    void love(int a);
}

class Love2 implements Ilove{//2.外部实现类来实现接口
    @Override
    public void love(int a) {
        System.out.println(a + "--->I love you外部类");
    }
}

public class TestLambda2 {

    static class Love3 implements Ilove{//3.静态局部类来实现接口
        @Override
        public void love(int a) {
            System.out.println(a + "--->I love you 静态局部类");
        }
    }

    public static void main(String[] args) {

        class Love4 implements Ilove{//4.局部内部类
            @Override
            public void love(int a) {
                System.out.println(a + "--->I love you 局部内部类");
            }
        }


        Ilove love2 = new Love2();//外部类
        love2.love(2);

        Ilove love3 = new Love3();//静态局部类
        love3.love(3);

        Ilove love4 = new Love4();//静态局部类
        love4.love(4);


        Ilove love5 = new Ilove(){//5.匿名局部类。 必须借助接口或者父类
            @Override
            public void love(int a) {
                System.out.println(a + "--->I love you 匿名局部类");
            }
        };
        love5.love(5);

        Ilove love6 = (int a) -> {//6.lambda表达式。 必须借助接口或者父类
            System.out.println(a + "--->I love you lambda表达式");

        };
        love6.love(6);

        Ilove love7 =(a)-> {//7.lambda表达式,进一步简化,简化参数类型
            System.out.println(a + "--->I love you lambda表达式进一步简化参数类型");};
        love7.love(7);

        Ilove love8 =a-> {//8.lambda表达式,进一步简化,简化参数类型,简化括号
            System.out.println(a + "--->I love you lambda表达式进一步简化参数类型,简化括号");};
        love7.love(8);

        //9.lambda表达式,进一步简化,简化参数类型,简化括号,简化花括号。
        //只能代码为一行时候,才能简化花括号
        Ilove love9 =a-> System.out.println(a + "--->I love you lambda表达式进一步简化参数类型,简化括号,简化花括号");
        love7.love(9);

    }
}

/*
总结:
    1.lambda表达式,只有一行代码才能简化成一行,如果有多行,就必须用花括号(代码块)。
    2。lambda表达式必须是函数式接口
    3.多个参数要去掉的话必须全部去掉定义类型。比如(int a,int b)变成a,b。而不能(a,int b)。建议多个参数时候加括号
 */

4.静态代理模式(在 2.2 Runnable中提到过代理)

静态代理
/*
静态代理模式的总结:
    1.真实对象和代理对象,都要实现同一个接口
    2.代理对象要代理真实角色。代理对象要有真实对象的参数
        比如下文:private Marry target;  使用构造器传入真实角色,然后使用真实角色的重写部分

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

interface Marry{//定义一个接口
    void happyMarry();
}

//我是一个真实角色
class You implements Marry{
    @Override
    public void happyMarry() {
        System.out.println("lu结婚了,超开心");
    }
}

//婚庆公司代理角色,帮助代理
class WweddingCompany implements Marry{

    private Marry target;//目标结婚对象

    public WweddingCompany(Marry target) {//构造器
        this.target = target;//使用这个类的 Marry 类型的target,target这边传的是You,
    }

    @Override
    public void happyMarry() {
        before();
        this.target.happyMarry();//执行You的重写部分
        after();
    }

    private void after() {//结婚之前
        System.out.println("结婚之前");
    }

    private void before() {//结婚之后
        System.out.println("结婚之后");
    }
}

public class demo01 {
    public static void main(String[] args) {
        You you = new You();//创建真实对象的变量
        WweddingCompany wweddingCompany = new WweddingCompany(you);//真实对象传入代理对象的构造器,进行初始化
        wweddingCompany.happyMarry();
        
        //上面三行可以简写为:new WweddingCompany(new You()).happyMarry();
        
        /*
        new WweddingCompany(new You()).happyMarry();
        WweddingCompany是一个代理,代理new You(),调用WweddingCompany里面的happyMarry()方法

        new Thread(()-> System.out.println("我爱你")).start();
        Thread是一个代理,代理()-> System.out.println("我爱你"),调用Thread里面的start()方法
        ()-> System.out.println("我爱你")用了Lanbda表达式,它可以表现为实现Runnable接口的实现,Thread也是Runnable接口的实现。
        所以Thread才可以代理。
         */
    }
}

5. 线程状态

5.1 线程的运动状态与运行

  • 五大状态:创建、就绪、运行、阻塞、死亡

在这里插入图片描述

在这里插入图片描述

5.2 停止线程方法(使用标志位)

在这里插入图片描述

public class Stop implements Runnable{//Stop应用到Runnable接口之中
    //1.设置一个标识位
    private boolean flag = true;
    //2.设置一个方法来停止线程
    public void stop()
    {
        this.flag = false;
    }

    @Override
    public void run() {//重写接口的方法
        int i = 1;
        System.out.println("线程被启动了");
        //3.线程体使用该标志
        while(flag)
        {
            System.out.println("run thread "+ (i++));
        }
    }

    public static void main(String[] args) {
        Stop s = new Stop();
        new Thread(s).start();//启动线程了
        for (int i = 0; i < 100; i++) {
            System.out.println("main跑了:" + i);
            if (i==50)
            {
                s.stop();
                System.out.println("线程终止");
            }
        }
    }
}

5.3 线程的休眠方法(Sleep)

在这里插入图片描述

  • S l e e p 案例:模拟倒计时 \textcolor{red}{Sleep案例:模拟倒计时} Sleep案例:模拟倒计时
import java.text.SimpleDateFormat;
import java.util.Date;
//模拟倒计时
public class Sleep2 {
    //模拟倒计时
    public void tenDown() throws InterruptedException {
        int num=10;
        while (true)
        {
            Thread.sleep(1000);//每1秒钟倒计时一次
            System.out.println(num--);
            if (num<=0)
            {
                break;
            }
        }
    }

    public static void main(String[] args) {
        Sleep2 sleep2 = new Sleep2();
        try {
            sleep2.tenDown();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

S l e e p 案例:每隔一秒打印当前时间 \textcolor{red}{Sleep案例:每隔一秒打印当前时间} Sleep案例:每隔一秒打印当前时间

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

    public static void main(String[] args) {
        Date startTime =new Date(System.currentTimeMillis());//获取系统当前时间
        while (true) {
            try {
                Thread.sleep(1000);//停止一秒
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));//打印系统当前时间
                startTime =new Date(System.currentTimeMillis());//更新时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
}

S l e e p 案例:模拟网络延时 ( 这个火车票案例,与 2.2 的 R u n n a b l e 一样,只是加了 s l e e p 休眠线程 ) \textcolor{red}{Sleep案例:模拟网络延时(这个火车票案例,与2.2的Runnable一样,只是加了sleep休眠线程)} Sleep案例:模拟网络延时(这个火车票案例,与2.2Runnable一样,只是加了sleep休眠线程)

//模拟网络延时:放大系统的发生性。
public class Sleep implements Runnable{
    private int ticketNum=10;//火车的票数
    @Override//重写线程
    public void run() {
        while (true)
        {
            if(ticketNum<=0)
            {
                break;
            }

            try {//
                Thread.sleep(200);//线程的延时 200毫秒
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第:"+ ticketNum +" 张票");
            //Thread.currentThread().getName()获得当前执行线程的名字,下面有给起名
            ticketNum=ticketNum-1;
        }
    }

    public static void main(String[] args) {
        Sleep t = new Sleep();//只需要new一个。对象创建一个TestThread2的runnable接口实例化对象
        //多个线程操作一个对象,会不安全
        new Thread(t,"小明").start();//Thread线程可以起名字,就是线程的名字
        new Thread(t,"小王").start();
        new Thread(t,"小红").start();
    }
}

5.4 线程的礼让(yield)

  • 礼让:申请CPU重新调度

在这里插入图片描述

  • 线程礼让案例
//创建一个其它的类来实现Runnable接口
//线程的礼让.礼让不一定成功
public class Yield_test{

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

class MyYield implements Runnable{//定义一个类去继承Runnable
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}
  • 上面那个案例另一种写法
//自身类实现Runnable接口
//线程的礼让.礼让不一定成功
public class Yield_test implements Runnable{//使public class类去继承Runnable
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始执行");
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程停止执行");

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

5.5线程的强制执行方法(Join,插队)

在这里插入图片描述

//线程的强制执行 Join
//一开始run与main主线程是并发执行的 ,但是Join强制让main进入阻塞,优先运行run,等run运行完之后才继续运行main 
public class Join implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        //启动我们的线程
        Join join = new Join();
        Thread thread = new Thread(join);
        thread.start();

        //主线程运动
        for (int i = 0; i < 100; i++) {
            System.out.println("main:"+i);
            if (i==20)
            {
                System.out.println("threat join");
                thread.join();//强制线程thread插队,thread先执行,要等thread执行完,才能走其它的线程。
            }
        }

    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("run :"+ i);
        }
    }
}

5.6 线程的状态监测

Thread thread = new Thread();//创建一个新线程的对象
Thread.State state = thread.getState();//获取线程的状态

在这里插入图片描述

public class TestState {
    public static void main(String[] args) throws InterruptedException {
        //1.new 状态
        Thread thread = new Thread(()->{//lambda方法,不用另外定义run重写方法
            for (int i = 0; i < 4; i++) {
                try {
                    Thread.sleep(1_000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
            System.out.println("//");
        });

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

        //2.观察启动状态
        thread.start();//启动线程
        state = thread.getState();
        System.out.println(state);//RUN

        while (state != Thread.State.TERMINATED)//直线线程不终止,就一直输出线程的状态
        {
            Thread.sleep(50);
            state = thread.getState();
            System.out.println(state);
        }

        //3.观察终止状态
        state = thread.getState();
        System.out.println(state);
    }
}

5.7线程的优先级

在这里插入图片描述

package com.Thread.state;
//线程的优先级 有1-10 ,不设定是默认5
//Thread.MAX_PRIORITY最高级10  Thread.MIN_PRIORITY 1          Thread.NORM_PRIORITY 5
//不定义线程的名字的话,Thread.currentThread().getName()获取线程名默认是从Thread 0开始
public class Priority {
    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 0开始
        Thread t1 = new Thread(myPriority,"t1");
        Thread t2 = new Thread(myPriority,"t2");
        Thread t3 = new Thread(myPriority,"t3");
        Thread t4 = new Thread(myPriority,"t4");
        Thread t5 = new Thread(myPriority,"t5");
        Thread t6 = new Thread(myPriority,"t6");
        Thread t7 = new Thread(myPriority,"t7");
        Thread t8 = new Thread(myPriority,"t8");
        Thread t9 = new Thread(myPriority,"t9");
        Thread t10 = new Thread(myPriority,"t10");

        //先设置线程的优先级,再启动
        t1.setPriority(1);
        t1.start();

        t2.setPriority(2);
        t2.start();

        t3.setPriority(3);
        t3.start();

        t4.setPriority(4);
        t4.start();

        t5.setPriority(5);
        t5.start();

        t6.setPriority(6);
        t6.start();

        t7.setPriority(7);
        t7.start();

        t8.setPriority(8);
        t8.start();

        t9.setPriority(9);
        t9.start();

        t10.setPriority(Thread.MAX_PRIORITY);
        t10.start();
    }
}


class MyPriority implements Runnable{
    @Override
    public void run() {
        //Thread.currentThread().getPriority()获取当前线程的优先级
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
    }
}

5.8守护(daemon)线程

在这里插入图片描述

//测试守护线程
//上帝守护这你
public class Deamon {
    public static void main(String[] args) {
        God god = new God();//创建God对象
        You you = new You();//创建you对象

        Thread thread = new Thread(god);
        //设定保护线程。默认是false,表示用户线程,正常的线程都是用户线程。要自己设定true
        //自己设定true就是守护线程
        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 < 30; i++) {//人一生36500天
            System.out.println("你一生都开心的活着");
        }

        System.out.println("Goodbye world");//过完一生
    }
}

6.线程同步 synchronized(重点)

6.1并发 和 线程同步基本概念

  • 并发:同一个对象被多个线程操控 , 会造成不安全的现象 \textcolor{red}{并发:同一个对象被多个线程操控,会造成不安全的现象} 并发:同一个对象被多个线程操控,会造成不安全的现象

  • 线程的同步就是用来解决并发问题 \textcolor{red}{线程的同步就是用来解决并发问题} 线程的同步就是用来解决并发问题

//并发例子
TestSleep ticket = new TestSleep();
//ticket对象被t1,t2,t3多个线程控制。
Thread t1 = new Thread(ticket,"a");
Thread t2 = new Thread(ticket,"b");
Thread t3 = new Thread(ticket,"c");

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

//或者另一种写法
/*
new Thread(ticket,"a").start();
new Thread(ticket,"b").start();
new Thread(ticket,"c").start();
*/
  • 线程同步的基本概念 \textcolor{red}{线程同步的基本概念} 线程同步的基本概念

在这里插入图片描述

6.2 队列 与 锁(线程同步的形成条件)

线程同步形成条件 : 队列 + 锁。让线程排成队列,当一个线程访问的时候,用锁防止其它线程访问,使线程按照队列按顺序访对象。

锁:每一个对象都有属于自己的一把锁。

在这里插入图片描述

6.3线程同步的三大不安全案例

  • 买票的不安全案例
//不安全的买票
//同一对象可以理解为票总数,当多个线程都去共用一个对象时,就是去买票,票数会减少,可能会出现拿到同一张票,抢票的现象。
//线程不安全,可能会出现买了负数的票。当都剩下最后一张的时候,他们都以为买了最后一张,但是只有一个人买到,其他人是0,或者-1
public class UnsafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket,"a").start();
        new Thread(buyTicket,"b").start();
        new Thread(buyTicket,"c").start();
    }
}

class BuyTicket implements Runnable{
    int ticketnum = 10;//票
    boolean flag = true;//外部停止方式
    @Override
    public void run() {
        //买票
        while (flag)
        {
            try {
                buy();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }

    private void buy() throws InterruptedException {
        //判断是否有票
        if (ticketnum<=0) {
            flag = false;
            return;
        }
        Thread.sleep(100);
        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketnum--);
    }
}
  • 银行的不安全取钱
//不安全的取钱
//两个人去银行
//看他们同时访问了哪一个对象,这个对象的里面的值可能会出现不安全的现象
public class UnsafeBank {
    public static void main(String[] args) {
        Acount acount = new Acount(100,"结婚基金");
        Drawing man = new Drawing(acount,50,"男孩");
        Drawing girl = new Drawing(acount,100,"女孩");
        man.start();
        girl.start();
    }
}

//账户
class Acount{
    int money;//余额
    String name;
    public Acount(int money,String name) {
        this.money = money;
        this.name = name;
    }
}

//银行:模拟取款
class Drawing extends Thread{
    Acount acount;//账户
    //取了多少钱
    int drawMoney;
    //现在手里多少钱
    int nowmoney;
    public Drawing(Acount acount,int drawMoney,String name){
        super(name);
        this.drawMoney=drawMoney;
        this.acount=acount;
    }
    //重写run方法来表示取钱操作
    @Override
    public void run() {
        if(acount.money-drawMoney<0)//判断账户里的钱是否够
        {
            System.out.println(Thread.currentThread().getName()+"钱不够了取不了");
            return;
        }
        //卡内余额 = 余额 - 你取的钱
        acount.money=acount.money-drawMoney;
        //sleep放大安全问题的发生性
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        nowmoney =nowmoney +drawMoney;
        //你手里的钱
        System.out.println("账户余额:"+acount.name+" = "+acount.money);
        //Thread.currentThread().getName() =  getName()  Drawing继承了Thread,可以用它的方法
        //Thread.currentThread()返回的还是Thread ,所以this=Thread.currentThread().
        System.out.println(getName()+"手里的钱 "+nowmoney);
    }
}
  • 集合list的不安全案例改写(没学过,后面从书上补)
//还没学过,到时候看书补一下
import java.util.ArrayList;
import java.util.List;

public class UnsafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();//传入一个泛型

        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                list.add(Thread.currentThread().getName());
            }).start();
        }
        System.out.println(list.size());
    }
}

6.4 synchronized用法 保证线程的同步

  • synchronized是一把锁

  • synchronized方法有两种用法:synchronized方法 与 synchronized块

6.4.1 synchronized同步方法

在这里插入图片描述

买票的不安全案例改写

//不安全的买票用synchronized方法进行同步
//同一对象可以理解为票总数,当多个线程都去共用一个对象时,就是去买票,票数会减少,可能会出现拿到同一张票,抢票的现象。
//线程不安全,可能会出现买了负数的票。当都剩下最后一张的时候,他们都以为买了最后一张,但是只有一个人买到,其他人是0,或者-1

public class UnsafeBuyTicket {

    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();

        new Thread(buyTicket,"a").start();
        new Thread(buyTicket,"b").start();
        new Thread(buyTicket,"c").start();
    }
}

class BuyTicket implements Runnable{
    int ticketnum = 10;//票
    boolean flag = true;//外部停止方式
    @Override
    public  void run() {
        //买票
        while (flag)
        {
            try {
                Thread.sleep(100);
                buy();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

    }
//============================================================
    //synchronized同步方法,锁的是this对象,对象本身
    private synchronized void buy() throws InterruptedException {
        //判断是否有票
        if (ticketnum<=0) {
            flag = false;
            return;
        }
        //买票
        System.out.println(Thread.currentThread().getName()+"拿到"+ticketnum--);
    }
}

6.4.2 synchronized同步块

在这里插入图片描述

银行的不安全取钱

package com.Thread.syn;
//不安全的取钱,使用synchronized同步块来进行锁处理
//两个人去银行
//看他们同时访问了哪一个对象,这个对象的里面的值可能会出现不安全的现象
public class UnsafeBank {
    public static void main(String[] args) {
        Acount acount = new Acount(1000,"结婚基金");
        Drawing man = new Drawing(acount,50,"男孩");
        Drawing girl = new Drawing(acount,100,"女孩");
        man.start();
        girl.start();
    }
}

//账户
class Acount{
    int money;//余额
    String name;
    public Acount(int money,String name) {
        this.money = money;
        this.name = name;
    }
}

//银行:模拟取款
class Drawing extends Thread{
    Acount acount;//账户
    //取了多少钱
    int drawMoney;
    //现在手里多少钱
    int nowmoney;
    public Drawing(Acount acount,int drawMoney,String name){
        super(name);
        this.drawMoney=drawMoney;
        this.acount=acount;
    }
    //重写run方法来表示取钱操作
    //synchronized(obj) 用synchronized方法块来锁定方法内的代码,obj是同步监视器,用共享资源作为同步监视器
    //锁的对象就是变化的量,需要增删改查的对象
    @Override
    public void run() {
        
        synchronized (acount)//锁的对象就是变化的量,需要增删改查的对象
        {
            if(acount.money-drawMoney<0)//判断账户里的钱是否够
            {
                System.out.println(Thread.currentThread().getName()+"钱不够了取不了");
                return;
            }
            //卡内余额 = 余额 - 你取的钱
            acount.money=acount.money-drawMoney;
            //sleep放大安全问题的发生性
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            nowmoney =nowmoney +drawMoney;
            //你手里的钱
            System.out.println("账户余额:"+acount.name+" = "+acount.money);
            //Thread.currentThread().getName() =  getName()  Drawing继承了Thread,可以用它的方法
            //Thread.currentThread()返回的还是Thread ,所以this=Thread.currentThread().
            System.out.println(getName()+"手里的钱 "+nowmoney);
        }

    }
}

集合list的不安全案例改写(没学过,后面从书上补)

//还没学过,到时候看书补一下
import java.util.ArrayList;
import java.util.List;

public class UnsafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();//传入一个泛型

        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                synchronized(list) {//synchronized块,监视的对象时list
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }

        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

6.5 死锁

死锁:多个线程各自占用一些共享资源,并且互相等待其它线程占有的资源,而导致多个线程都在等待对方释放资源,都停止执行的情况。或者某一个同步块拥有"两个以上的对象的锁"

package com.Thread;
//死锁:多个线程互相抱着对方需要的资源,然后形成僵持

public class Deadlock {
    public static void main(String[] args) {
        Makeup g1 = new Makeup(0, "红");
        Makeup g2 = new Makeup(1, "绿");
        g1.start();
        g2.start();
    }
}

//口红
class Liostick{}

//镜子
class Mirror{}

//化妆
class Makeup extends Thread{
    int choice;
    String girlName;

    Makeup(int choice,String girlName)//构造器初始化值
    {
        this.choice = choice;
        this.girlName = girlName;
    }

    //需要的口红,镜子的资源只有一份,用static只有一份
    static Liostick liostick = new Liostick();
    static Mirror mirror = new Mirror();

    //化妆,互相持有对方的锁,需要对方的资源
    private void makeup() throws InterruptedException {
        if (choice==0){
            synchronized (liostick)//获得口红的锁
            {
                System.out.println(this.girlName+"获得口红的锁");
                Thread.sleep(1000);
                //拿到了口红,还想继续去拿镜子,两个线程僵持着产生死锁
//                synchronized (mirror){//一秒钟后获得镜子的锁
//                    System.out.println(this.girlName+"获得镜子的锁");
                }

            //死锁解决。先去拿了口红,拿完口红就释放掉口红(就是口红放回去),然后再去拿镜子,拿完在释放掉。
            synchronized (mirror){
                System.out.println(this.girlName+"获得镜子的锁");
            }
        }
        else {
            synchronized (mirror)//获得口红的锁
            {
                System.out.println(this.girlName+"获得镜子的锁");
                Thread.sleep(2000);
                //拿到了镜子,还想继续去拿口红,两个线程僵持着产生死锁
//                synchronized (liostick){//一秒钟后获得镜子的锁
//                    System.out.println(this.girlName+"获得口红的锁");
                }
            //死锁解决。先去拿了镜子,拿完镜子就释放掉镜子(就是镜子放回去),然后再去拿口红,拿完口红在释放掉。
            synchronized (liostick){
            System.out.println(this.girlName+"获得口红的锁");
        }

    }
    }

    @Override
    public void run() {
        try {
            makeup();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

在这里插入图片描述

6.6 Lock锁(和synchronized一样也可以进行线程同步)

在这里插入图片描述

在这里插入图片描述

package com.Thread;

import java.util.concurrent.locks.ReentrantLock;
//测试Lock锁
public class TestLock {
    public static void main(String[] args) {

        Testlock2 testlock2 = new Testlock2();
        //三个线程同时操控一个对象
        new Thread(testlock2,"我").start();
        new Thread(testlock2,"你").start();
        new Thread(testlock2,"他").start();
    }

}

class Testlock2 implements Runnable{

    int ticketnum =100;
    //定义lock锁private final私人常量,更加安全
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true)
        {
            //加锁要写在try外面,不安全代码块卸载try里面。解锁放在finally里面
            lock.lock();//加锁
            try {
                if (ticketnum>0)
                {
//                    try {
//                        Thread.sleep(500);
//                    } catch (InterruptedException e) {
//                        throw new RuntimeException(e);
//                    }
                    System.out.println(Thread.currentThread().getName()+ " "+ticketnum--);
                }else {
                    break;
                }
            } finally {
                lock.unlock();
            }

        }
    }
}

在这里插入图片描述

7.线程协作通信(生产者与消费者)

7.1线程通信的分析

在这里插入图片描述

7.2线程通信的方法

在这里插入图片描述

7.3线程通信的二种解决方式(管程法、交通灯法)

7.3.1 管程法

在这里插入图片描述

//测试:生产者与消费者模型-->利用缓冲区解决;管程法
//需要四个对象:生产者,消费者,产品,缓冲区

//缓冲区
class SynContainer{
    //需要一个容器大小,十只鸡
    Chicken[] chickens = new Chicken[10];
    //容器计数器
    int count =0;

    //生产者生产产品。生产与消费线程控制产品数量,需要用到同步synchronized
    public synchronized void push(Chicken chicken){
        //如果容器满了,就等待消费者消费
        while(count==chickens.length){//唤醒不能用if,因为唤醒后不会执行if的语句,而能执行while语句
            //生产者等待
            try {
                this.wait();//如果已经满了,生产者等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        //如果没有满就丢入产品
        chickens[count] = chicken;
        count++;

        //可以通知消费者消费 this.notifyAll();唤醒这个对象上所以等待的线程
        this.notifyAll();
    }

    //消费者消费产品。生产与消费线程控制产品数量,需要用到同步synchronized
    public synchronized Chicken pop(){
        //判断能够消费
        while  (count==0)
        {
            //等待生产者生产,消费者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
            //如果可以消费就消费
        count--;
        Chicken chicken = chickens[count];//消费的那只鸡


        //通知生产者生产
        this.notifyAll();
        return chicken;

    }
}

//生产者
class Productor extends Thread{
    //生产者需要容器
    SynContainer container;
    public Productor(SynContainer container)//构造器
    {
        this.container=container;
    }
    //生产
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {

            container.push(new Chicken(i));
            System.out.println("生产了第:"+i+" 只鸡");
        }
    }
}

//消费者
class Consumer extends Thread{
    //消费者则需要容器
    SynContainer container;
    public Consumer(SynContainer container)//构造器
    {
        this.container=container;
    }

    //消费
    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            System.out.println("消费了第几只鸡:"+ container.pop().id+"只鸡");
        }

    }
}

//产品:鸡
class Chicken{
    int id;//产品编号

    public Chicken(int id) {
        this.id = id;
    }
}

public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();

        new Productor(synContainer).start();
        new Consumer(synContainer).start();
    }
}

7.3.2 交通灯法

在这里插入图片描述

//信号灯 :标志位
public class TestPc2 {
    public static void main(String[] args) {
        TV tv = new TV();

        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//生产者--》演员
class Player extends Thread{
    TV tv;
    public Player(TV tv)
    {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            if (i%2==0)
            {
                this.tv.play("快乐大本营播放中");
            }else {
                this.tv.play("抖音");
            }
        }
    }
}

//消费者--》观众
class Watcher extends Thread{
    TV tv;
    public Watcher(TV tv)
    {
        this.tv = tv;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            tv.watch();
        }
    }
}

//产品--》节目
class TV{
    String voice;//表演节目
    boolean flag = true;//为真演员录播,为假 演员等待
    //演员录播,观众等待  T
    //观众观看,演员等待  F

    //演员表演
    public synchronized void play(String voice)
    {
        if (flag==false)//观众观看,演员等待
        {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("演员表演了:"+ voice);//演员表演了voice
        this.voice=voice;
        //通知观众观看
        this.flag=!this.flag;
        this.notifyAll();//唤醒观众
    }

    //观众观看
    public synchronized void watch()
    {
        if (flag==true)//flag==true 演员表演,观众等待
        {
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        System.out.println("观众观看了:" +voice);
        this.flag=!this.flag;
        this.notifyAll();
    }
}

8.线程池

在这里插入图片描述

在这里插入图片描述

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

//线程池
public class TestPool {
    public static void main(String[] args) {
        //1.创建服务,创建线程池,里面可以放十个线程
        ExecutorService service = Executors.newFixedThreadPool(10);

        //执行命令,执行四个线程的命令
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());

        //2.关闭连接
        service.shutdown();
    }
}

class MyThread implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 4; i++) {
            System.out.println(Thread.currentThread().getName()+"= "+i);
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值