狂神说Java笔记--多线程详解部分笔记


传送门==>B站遇见狂神说Java多线程详解

做笔记时有的知识点并没有整理;


1.线程创建之继承Thread类

  • 首先自定义线程类继承Thread类;
  • 然后是重写run( )方法;编写线程执行体
  • 然后创建自定义线程对象;使用start()方法启动线程

注意:线程开启并不一定是直接就执行;得看CPU的调度,或者说的根据CPU的实际资源情况;

代码练习:
创建MyThread

/**
 * 线程创建之继承Thread类;
 */
//1.首先继承Thread类;
public class MyThread01 extends Thread {
    //2.然后是重写run方法;编写执行的线程;
    @Override
    public void run() {
        //run方法 线程的执行体;
        for (int i = 0; i < 100; i++) {
            System.out.println("我是run方法->"+i);
        }
    }

    //mian方法;(主线程)
    public static void main(String[] args) {
        //创建线程对象;
        MyThread01 myThread01=new MyThread01();
        //3.使用start方法启动线程;
        myThread01.start();

        //main方法 线程的执行体;
        for (int i = 0; i < 100; i++) {
            System.out.println("我是main方法->"+i);
        }
    }
}

线程交替执行

在这里插入图片描述

而如果不使用start方法启动线程呢;直接在main方法中调用run方法;那就按着方法的调用顺序执行了,依次有序地执行

在这里插入图片描述


图片下载练习
//继承Thread类;
public class MyThread02 extends Thread{
    //定义网页地址和文件名;
    private String url;
    private String name;
    //构造方法;初始化;
    public MyThread02(String url, String name) {
        this.url = url;
        this.name = name;
    }

    //重写run方法
    @Override
    public void run() {
        //创建下载器对象;
        WebDownLoader wdl=new WebDownLoader();
        //调用下载方法;
        wdl.dowLoade(url,name);
        System.out.println("下载文件名=>"+name);
    }

    //主方法;
    public static void main(String[] args) {
        MyThread02 myT1=new MyThread02("https://www.baidu.com/link?url=dvRgPF9UDWw42jQF0lr8EnKMw2XfU_NKhWqanB4wL85sqwWrGBVnBAGd3zBJUJ0JZa0d3ryBEd6FCiZfrG1ax0o3EkygROiBPwUrzEFRoC4DK6S5DSiMz1Q1Ge_O8vyNbE8IzW1Kn2WGbHDgGzx4yhlQsOadV1rFqemy4Os4lhBMAxGLXZ2CY5ftLFMYzxUgZbFIw5qtkPUajas-nPD4yyk118flZrp6Dj6HOAfwKB-X0xbxH2ttXMocCGzVHuwGm0a0Orw8A_cqyOm3tDvXMhKu3YSYHtKDkisZqupVBI3pJhhvP97RCXacC0jtynAXvUrPz4OAhT_QvMfWp7akjFP5gBFiK_5Tt2BwRchJ1YgenbZwqGucGAopjBtmlKeAKOmkaVJ12wiG6u7Y85YNIlGo__O0ZgurYtRb4WbOZTxsBX_e0XrBB0klH2V2BJM3YuEyMpc_6sZdwhYnj4JCltSh1oca5jDlQ51Tu2LHqI-SeDhvi_ioBAno4NzkQqEI_rhRIJSUqb7EVBjO7sTEB8qohgcbM9ZtD-3fqTYTRZBQ97W0o5p2tUTEVuXDTM0FeunmEEoiaLeBCSJbB8to09OYCCy9C-e2CZyaaiIAnXvAlAILYMaHh0a1ANXLzFR1&wd=&eqid=9a0261300016198200000005611d721a","图1.jpg");
        MyThread02 myT2=new MyThread02("https://www.baidu.com/link?url=dvRgPF9UDWw42jQF0lr8EnKMw2XfU_NKhWqanB4wL85sqwWrGBVnBAGd3zBJUJ0JZa0d3ryBEd6FCiZfrG1ax0o3EkygROiBPwUrzEFRoC4DK6S5DSiMz1Q1Ge_O8vyNbE8IzW1Kn2WGbHDgGzx4yhlQsOadV1rFqemy4Os4lhBMAxGLXZ2CY5ftLFMYzxUgZbFIw5qtkPUajas-nPD4yyk118flZrp6Dj6HOAfwKB-X0xbxH2ttXMocCGzVHuwGm0a0Orw8A_cqyOm3tDvXMhKu3YSYHtKDkisZqupVBI3pJhhvP97RCXacC0jtynAXvUrPz4OAhT_QvMfWp7akjFP5gBFiK_5Tt2BwRchJ1YgenbZwqGucGAopjBtmlKeAKOmkaVJ12wiG6u7Y85YNIlGo__O0ZgurYtRb4WbOZTxsBX_e0XrBB0klH2V2BJM3YuEyMpc_6sZdwhYnj4JCltSh1oca5jDlQ51Tu2LHqI-SeDhvi_ioBAno4NzkQqEI_rhRIJSUqb7EVBjO7sTEB8qohgcbM9ZtD-3fqTYTRZBQ97W0o5p2tUTEVuXDTM0FeunmEEoiaLeBCSJbB8to09OYCCy9C-e2CZyaaiIAnXvAlAILYMaHh0a1ANXLzFR1&wd=&eqid=9a0261300016198200000005611d721a","图2.jpg");
        MyThread02 myT3=new MyThread02("https://www.baidu.com/link?url=dvRgPF9UDWw42jQF0lr8EnKMw2XfU_NKhWqanB4wL85sqwWrGBVnBAGd3zBJUJ0JZa0d3ryBEd6FCiZfrG1ax0o3EkygROiBPwUrzEFRoC4DK6S5DSiMz1Q1Ge_O8vyNbE8IzW1Kn2WGbHDgGzx4yhlQsOadV1rFqemy4Os4lhBMAxGLXZ2CY5ftLFMYzxUgZbFIw5qtkPUajas-nPD4yyk118flZrp6Dj6HOAfwKB-X0xbxH2ttXMocCGzVHuwGm0a0Orw8A_cqyOm3tDvXMhKu3YSYHtKDkisZqupVBI3pJhhvP97RCXacC0jtynAXvUrPz4OAhT_QvMfWp7akjFP5gBFiK_5Tt2BwRchJ1YgenbZwqGucGAopjBtmlKeAKOmkaVJ12wiG6u7Y85YNIlGo__O0ZgurYtRb4WbOZTxsBX_e0XrBB0klH2V2BJM3YuEyMpc_6sZdwhYnj4JCltSh1oca5jDlQ51Tu2LHqI-SeDhvi_ioBAno4NzkQqEI_rhRIJSUqb7EVBjO7sTEB8qohgcbM9ZtD-3fqTYTRZBQ97W0o5p2tUTEVuXDTM0FeunmEEoiaLeBCSJbB8to09OYCCy9C-e2CZyaaiIAnXvAlAILYMaHh0a1ANXLzFR1&wd=&eqid=9a0261300016198200000005611d721a","图3.jpg");
        //启动线程;
        myT1.start();
        myT2.start();
        myT3.start();
    }
}

//创建下载器;
class WebDownLoader{
    //下载方法;
    public void dowLoade(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("下载方法有问题, IO异常");
        }
    }
}

运行;下载时并不会按照顺序,

在这里插入图片描述

在这里插入图片描述


2.线程创建之实现Runnable接口

可避免单继承的局限性;方便一个对象被多个线程使用.

  • 自定义类实现Runnable接口;
  • 重写run()方法,编写线程执行体
  • 创建自定义线程对象放入Thread对象中,start()方法启动线程

代码练习

/**
 * 创建线程之实现Runnable接口
 */
//实现Runnable接口;
public class MyThread03 implements Runnable {

    //重写run方法;
    @Override
    public void run() {
        //run方法 线程的执行体;
        for (int i = 0; i < 100; i++) {
            System.out.println("我是run方法->" + i);
        }
    }

    //主方法;
    public static void main(String[] args) {
        //创建实现类的对象;
        MyThread03 myThread03 = new MyThread03();
        //创建线程对象;启动线程
        new Thread(myThread03).start();

    //main方法 线程的执行体;
        for (int i = 0; i < 100; i++) {
            System.out.println("我是main方法->" + i);
        }
    }
}
买票案例

多个线程去操作同一份资源,线程不安全

//实现Runnable接口;
public class MyThreadToTicket implements Runnable {
    //定义车票数量;
    private int ticket=10;
    //重写run方法;
    @Override
    public void run() {
        while(true) {
            //票买完就停了;
           if(ticket<=0) {
                break;
            }

            try {
                //定义延时;
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Thread.currentThread().getName():可获取到当前线程的名字;
            System.out.println(Thread.currentThread().getName() + "::买到了第" + ticket-- + "个票");
        }
    }
    //主方法;
    public static void main(String[] args) {
        //创建自定义类的对象;
        MyThreadToTicket mt=new MyThreadToTicket();
        //创建Thread对象,启动线程;
        new Thread(mt,"阿杰").start();
        new Thread(mt,"阿猫").start();
        new Thread(mt,"阿伟").start();
    }
}

运行时,出现两个人买到同一张票的问题.

在这里插入图片描述


模拟龟兔赛跑
//实现Runnable接口;
public class TorAndRabRun implements Runnable {
    //定义胜利者;
    private static String win;

    //判断是否结束比赛; step:步数;
    private boolean over(int step){
        //若已有获胜者,则结束;
        if(win!=null){
            return true;
        }{
            //若已经跑完了,结束比赛;
            if(step>=100){
                win=Thread.currentThread().getName();
                System.out.println("获胜者" + win);
                return true;
            }
        }
        return false;
    }

    //重写run方法
    @Override
    public void run() {
        for (int i = 0; i <= 101; i++) {
            //模拟兔子睡觉;
            if(Thread.currentThread().getName().equals("兔纸")&&i%10==0){
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

            //若比赛结束,终止循环;
            if (over(i)) {
                break;
            }
            //Thread.currentThread().getName():会获取线程名;
            System.out.println(Thread.currentThread().getName()+"==>跑"+i+"米");
        }
    }
    //主方法;
    public static void main(String[] args) {
        //创建赛道对象;
        TorAndRabRun tar=new TorAndRabRun();

        //创建线程对象,启动线程;
        new Thread(tar,"乌龟").start();
        new Thread(tar,"兔纸").start();
    }
}

在这里插入图片描述


3.线程创建之实现Callable接口

  • 实现callable接口,需要返回值类型;
  • 重写call方法;需要抛出异常类型;
  • 创建自定义线程的对象;
  • 开启服务;
  • 提交执行;
  • 获取结果;
  • 关闭服务

使用之前的多线程下载图片案例;

/**
 * 线程创建之实现Callable接口;和前两种不同的是该方式可产生返回值;
 */
//实现Callable接口;
public class MyThread04 implements Callable<Boolean> {
    //定义网页地址和文件名;
    private String url;
    private String name;
    //构造方法;初始化;
    public MyThread04(String url, String name) {
        this.url = url;
        this.name = name;
    }

    //重写callable方法;
    @Override
    public Boolean call() throws Exception {
        //创建下载器对象;
       WebDownLoader wdl=new WebDownLoader();
        //调用下载方法;
        wdl.dowLoade(url,name);
        System.out.println("下载文件名=>"+name);
        return null;
    }

    //主方法
    public static void main(String[] args) throws ExecutionException, InterruptedException{
        //创建实现类的对象;
        MyThread04 myT1=new MyThread04("https://img-blog.csdnimg.cn/20210613214620230.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01yVHVtbnVz,size_16,color_FFFFFF,t_70","图片1.png");
        MyThread04 myT2=new MyThread04("https://img-blog.csdnimg.cn/20210613214620230.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01yVHVtbnVz,size_16,color_FFFFFF,t_70","图片2.png");
        MyThread04 myT3=new MyThread04("https://img-blog.csdnimg.cn/20210613214620230.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01yVHVtbnVz,size_16,color_FFFFFF,t_70","图片3.png");

        //开启服务;(创建线程池)
        ExecutorService es= Executors.newFixedThreadPool(3);
        //执行服务提交;
        Future<Boolean> ft1=es.submit(myT1);
        Future<Boolean> ft2=es.submit(myT2);
        Future<Boolean> ft3=es.submit(myT3);
        //获取返回结果;
        Boolean res1 = ft1.get();
        Boolean res2 = ft2.get();
        Boolean res3 = ft3.get();
        System.out.print(res1);
        System.out.print(res2);
        System.out.print(res3);
        //关闭服务;
        es.shutdownNow();
    }
}
//创建下载器;
class WebDownLoader{
    //下载方法;
    public void dowLoade(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("下载方法有问题, IO异常");
        }
    }
}

也是交替执行的;

在这里插入图片描述


4.静态代理模式

  • 真实对象(目标对象)和代理对象都要实现同一个接口;
  • 代理对象要代理真实角色;
  • 代理对象可实现真实对象不能完成的方法;
  • 真实对象专注自己的实现;
public class StaticProxy {
    public static void main(String[] args) {
        //代理        |  真实       |             需要完成的事情;
        //new Thread(()->System.out.println("完成")).start();

       /* //创建真实角色对象;
        Person ps=new Person();
        //创建代理角色对象;
        ProxyPerson prop=new ProxyPerson(ps);
        prop.Happy();*/

        //实际上,可精简为;
        //      代理    |  真实    |  需要完成的事情;
        new ProxyPerson(new Person()).Happy();
    }
}

//接口;
interface Marry{
    //抽象方法;
    void Happy();
}

//人;(真实角色)
class Person implements  Marry{

    @Override
    public void Happy() {
        System.out.println("真实角色出场");
    }
}

//中介;(代理角色)
class ProxyPerson implements Marry{
    //定义目标对象1;
    private Marry targetP;
    //构造方法;
    public ProxyPerson(Marry targetP) {
        this.targetP = targetP;
    }

    @Override
    public void Happy() {
        //调用之前要完成的方法;
        brfore();
        //执行方法;
        this.targetP.Happy();
        //调用之后要完成的方法
        after();
    }

    //之前要进行的方法;
    private void brfore() {
        System.out.println("前面要完成的事情;");
    }
    //之后要进行的方法;
    private void after() {
        System.out.println("后面要完成的事情;");
    }
}

5.Lambda表达式

lambda:
λ:希腊字母表第11;
使用目的是为了避免匿名内部类定义过多;实质上属于函数式编程;
-----------------------------------
(params)->expression [表达式]
(params)->statement (语句)
-----------------------------------
函数式接口:(Functional Interface)
----------------------------------
函数式接口定义:
只包含唯一的抽象方法;
可使用lambda表达式创建接口对象

练习;使用不同的方式实现输出接口方法;

public class DemoLamDa01 {
    //2.使用静态内部类;
    static class FunImpl02 implements FunMeth{
        @Override
        public void OnlyMethod() {
            System.out.println("FunImpl02静态内部类 实现了 FunMeth");
        }
    }

    public static void main(String[] args) {
        //3.使用局部内部类
        class FunImpl03 implements FunMeth{
            @Override
            public void OnlyMethod() {
                System.out.println("FunImpl03局部内部类 实现了 FunMeth");
            }
        }
        
        //(1)创建实现类对象;调用方法;
        FunMeth fm1=new FunImpl01();
        fm1.OnlyMethod();

        //(2)创建静态内部类对象;调用方法;
         fm1=new FunImpl02();
         fm1.OnlyMethod();


        //(3)创建局部内部类对象;调用方法
        fm1=new FunImpl03();
        fm1.OnlyMethod();

        //4.使用匿名内部类;
        fm1=new FunMeth() {
            @Override
            public void OnlyMethod() {
                System.out.println("FunImpl04匿名内部类 实现了 FunMeth");
            }
        };
        //(4)使用匿名内部类调用方法
        fm1.OnlyMethod();

        //(5)直接使用lamda表达式的方式
        fm1=() -> System.out.println("Lambda表达式 -->FunMeth");
        fm1.OnlyMethod();

    }
}

//定义函数式接口;(即只有一个抽象方法)
interface FunMeth{
    void OnlyMethod();
}

//1.接口的实现类;
class FunImpl01 implements FunMeth{
    @Override
    public void OnlyMethod() {
        System.out.println("FunImpl01 实现了 FunMeth");
    }
}
FunImpl01 实现了 FunMeth
FunImpl02静态内部类 实现了 FunMeth
FunImpl03局部内部类 实现了 FunMeth
FunImpl04匿名内部类 实现了 FunMeth
Lambda表达式 -->FunMeth

  • 前提是接口为函数式接口;(仅有一个抽象方法);
  • 若想要简化为一行lambda表达式,那么就只能输出一行代码;若需输出多行,lambda要加代码块包裹;
    简化lambda练习
  • 多个参数类型也可以简化,但是要用括号( )把参数包裹起来;
public class DemoLamDa02 {
    public static void main(String[] args) {
        int a1=11;
        int a2=12;
        int a3=13;
        int a4=14;

        //1.使用lambda表达式;
        FunMeth fm=(int a) -> { System.out.println("实现方法-且a1="+a1);};
        fm.OnlyMethod(a1);
        //2.简化;--去掉参数类型;
        FunMeth fm2=(a) -> { System.out.println("实现方法-且a2="+a2);};
        fm2.OnlyMethod(a2);
        //3.简化---去掉参数的括号();
        FunMeth fm3=a -> { System.out.println("实现方法-且a3="+a3);};
        fm3.OnlyMethod(a3);
        //4.简化---去掉大括号{};
        FunMeth fm4=(int a) ->  System.out.println("实现方法-且a4="+a4);
        fm4.OnlyMethod(a4);
    }
}
//函数式接口;
interface FunMeth{
    void OnlyMethod(int a);
}
实现方法-且a1=11
实现方法-且a2=12
实现方法-且a3=13
实现方法-且a4=14

6.线程状态:

在这里插入图片描述

(1)线程创建(新生成线程):
new Thread( ) ;创建后就进入新生状态;
(2)调用start方法,线程就进入就绪状态;
(3)就绪状态的线程,被调度后进入运行态;开始执行线程体中的代码;
(4)若调用了sleep( )睡眠,/wait( )等待,/同步锁定;线程进入阻塞态;只能等阻塞结束后,进入就绪态.
(5)线程中断/结束;进入死亡.


常用方法说明
setPriority( int state)修改线程优先级
static void sleep(long millis)指定毫秒时间内线程休眠
void join()等待线程终止
static void yield()暂停当前线程对象执行其他线程
void interrupt()中断线程
boolean isAlive()测试线程是否活着

线程停止
  • 使用一个标记变量终止线程;
  • 线程正常停止;(尽量不要死循环);
  • 尽量不使用stop方法;destory方法等过时方法;
//实现Runnable接口;
public class Mythread05 implements Runnable{
    //1.设置标志;
    private boolean flag=true;
    //重写run方法;
    @Override
    public void run() {
        int i=0;
        while (flag){
            System.out.println("自定义线程执行中...."+i++);
        }
    }
    //2.自定义线程停止方法;
    public void stop(){
        this.flag=false;
    }
    //主方法;
    public static void main(String[] args) {
        //创建实现类对象;
        Mythread05 myt5=new Mythread05();
        //创建线程对象,启动线程;
        new Thread(myt5).start();

        for (int i = 0; i < 500; i++) {
            System.out.println("主线程执行...."+i);
            if(i==200){
                myt5.stop();
                System.out.println("自定义线程停止");
            }
        }
    }
}

达到对应数字,自定义线程停止;

在这里插入图片描述


线程休眠 --sleep方法

使用sleep方法;

  • sleep(long time)指定线程休眠的时间(毫秒)
  • sleep设置的时间结束后,线程就跑到就绪态
  • sleep存在异常InterruptedException;
  • 可用来模拟倒计时;每个对象带有锁,但是sleep不会释放锁

模拟倒计时

//实现Runnable接口;
public class SleepTime {
    //自定义倒计时方法;
    public static void timeOver() throws InterruptedException {
        int time=10;
        //设置线程休眠时间1秒;
        while (true) {
            Thread.sleep(1000);
            System.out.println("倒计时=>"+time--);
            //到达指定数字,结束循环;
            if(time<=0){
                break;
            }
        }
    }
    //主方法;
    public static void main(String[] args) {
        //调用方法;
        try {
            timeOver();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这里插入图片描述

更新打印输出当前系统时间

//实现Runnable接口;
public class SleepNow {
    public static void main(String[] args) {
        //获取当前系统时间;
        Date startTime = new Date(System.currentTimeMillis());

        //进行循环;
        while (true){
            try {
                //使得线程休眠1秒;
                Thread.sleep(1000);
                //将当前时间格式化;
                System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
                //1秒更新一次时间;
                startTime=new Date(System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述


线程礼让—yield方法
  • 使得当前正在执行得线程暂停,但是不阻塞;
  • 线程礼让不一定会成功;是要让CPU重新调度;(可能就是这个线程刚进入就绪态,就马上获得了CPU资源,进入运行态,不给别的线程机会;所以说线程礼让不一定成功)
  • 线程由运行态到就绪态

线程礼让练习

//实现Runnable接口;
public class MyThread07 implements Runnable {
    //重写run方法;
    @Override
    public void run() {
        //Thread.currentThread().getName():获取当前线程名;
        System.out.println(Thread.currentThread().getName()+"线程开始运行");
        //调用线程礼让方法;
        Thread.yield();
        System.out.println(Thread.currentThread().getName()+"线程停止");
    }
    //主方法;
    public static void main(String[] args) {
        //创建实现类的对象;
        MyThread07 a = new MyThread07();
        MyThread07 b = new MyThread07();
        MyThread07 c = new MyThread07();
        //创建线程对象;启动线程
        new Thread(a,"壹号").start();
        new Thread(b,"贰号").start();
        new Thread(c,"叁号").start();
    }
}

线程礼让不一定成功;

在这里插入图片描述


线程强制执行 — join方法

join方法;让当前线程先执行结束,其他线程阻塞,再去执行其他线程;

练习

//实现Runnable接口;
public class MyThread08 implements Runnable {
    //重写run方法;
    @Override
    public void run() {
        //Thread.currentThread().getName():获取当前线程名;
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"线程运行==>"+i);
        }
    }
    //主方法;
    public static void main(String[] args) {
        //创建实现类的对象;
        MyThread08 a = new MyThread08();
        //创建线程对象;启动线程
        Thread t1=new Thread(a,"壹号");
        t1.start();
        
        for (int i = 0; i < 500; i++) {
            System.out.println("主线程执行=>"+i);
            if(i==200){
                //当数字到200时,线程1号强制执行;
                try {
                    t1.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

线程壹号强制执行

在这里插入图片描述


观测线程状态

在jdk说明文档Api中搜索线程状态–Thread.State

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

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

练习线程状态

public class MyThread09 {
    public static void main(String[] args) {
        Thread thread = new Thread(() ->{
        for (int i = 0; i < 2; i++) {
            try {
                //线程休眠;
                Thread.sleep(1000);
            } 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);

        //只要线程不停止就循环;
        while (state!= Thread.State.TERMINATED){
            try {
                //设置休眠;
                Thread.sleep(100);
                //更新线程状态且输出;
                state=thread.getState();
                System.out.println(state);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行可观测到线程的几个状态

NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
执行语句
TERMINATED

线程优先级

线程优先级的范围由1到10
其中; MIN_PRIORITY表示1,NORM_PRIORITY表示5;MAX_PRIORITY表示10

可使用方法获取线程优先级:getPriority;
设置优先级:setPriority(int priority)

java使用线程调度器监控程序中启动后进入就绪态的所有线程;线程调度器按照优先级决定应调度哪个线程执行

  • 注意:设置完优先级再启动线程;
  • 优先级低不一定就执行晚

练习

public class MyThread10 implements Runnable {
    //重写run方法;
    @Override
    public void run() {
        //Thread.currentThread().getName():获取当前线程名;
        //Thread.currentThread().getPriority():获取当前线程优先级;
        System.out.println(Thread.currentThread().getName()+"优先级为=>"+Thread.currentThread().getPriority());
    }

    public static void main(String[] args) {
        //主线程优先级为5
        System.out.println(Thread.currentThread().getName()+"优先级为=>"+Thread.currentThread().getPriority());
        //创建实现类对象
        MyThread10 myth=new MyThread10();
        //创建线程对象;
        Thread t1=new Thread(myth,"甲");
        Thread t2=new Thread(myth,"乙");
        Thread t3=new Thread(myth,"丙");
        Thread t4=new Thread(myth,"丁");
        Thread t5=new Thread(myth,"戊");
        //设置线程优先级;然后再启动线程;
        t1.setPriority(2);
        t1.start();
        t2.setPriority(10);
        t2.start();
        t3.setPriority(6);
        t3.start();
        t4.setPriority(5);
        t4.start();
        t5.setPriority(3);
        t5.start();
    }
}

优先级低的不一定就执行晚

在这里插入图片描述


守护线程
  • 线程分为用户线程与守护线程(daemon)
  • 虚拟机必须确保用户线程执行完毕;但是不需要等待守护线程执行完毕;
  • 常见的守护线程:监控内存,垃圾回收,后台记录日志
  • 使用方法setDaemon设置守护线程,该方法默认为false

练习

//守护线程学习;
public class GuardThread {
    public static void main(String[] args) {
        //创建上帝线程;
        God god=new God();
        Thread t1=new Thread(god);
        //设置上帝为守护线程;
        t1.setDaemon(true);
        //启动守护线程;
        t1.start();

        //创建人类线程;
        Person ps=new Person();
        //启动用户线程
        new Thread(ps).start();
    }
}
//模拟用户线程---人类;
class Person implements Runnable{
    //重写run方法;
    @Override
    public void run() {
        for (int i = 0; i < 36500; i++) {
            System.out.println("人类生活");
        }
        System.out.println("---人类生活结束----");
    }
}
//模拟守护线程---上帝;
class God implements Runnable{
    //重写run方法;
    @Override
    public void run() {
        while (true){
            System.out.println("<=上帝守护=>");
        }
    }
}

用户线程结束,但守护线程并未结束

在这里插入图片描述


7.线程同步机制

并发 :多个线程操作同一个对象

线程同步:

处理多线程问题时,多个线程访问同一个对象,并且某些线程还要修改这个对象;这时就需要线程同步,线程同步作为等待机制;多个需要同时访问当前对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕,下一个线程才会再次使用…

由于同一个进程的多个线程共享同一块存储空间,可能有访问冲突问题,为保证数据在方法中被访问时的正确性,在访问时加入锁机制(使用关键字synchronized),当一个线程获得对象的排他锁,独占了资源,其他线程必须等待,使用后释放锁才行.

  • 一个线程持有锁时,会导致其他所有需要这个锁的线程挂起;
  • 在多线程竞争情况下,加锁释放锁会导致一些上下文切换和调度延迟,引起性能问题;
  • 若一个高优先级的线程在等待一个低优先级的线程释放锁,会导致优先级倒置,引起性能问题

三个不安全线程案例

(1)不安全的买票案例:

可能出现多个人买到同一张票,或者负数票的结果.

public class NotSafeDemo01 {
    public static void main(String[] args) {
        //创建买票类对象;
        BuyTicket by=new BuyTicket();
        //创建线程且启动;
        new Thread(by,"阿智").start();
        new Thread(by,"阿猫").start();
        new Thread(by,"阿狗").start();
    }
}

//买票类;
class BuyTicket implements Runnable{
    //定义票的数量;
    private int ticket=10;
    //定义的标志位;
    boolean flag=true;
    //重写的run方法;
    @Override
    public void run() {
        //买票;
        while (flag){
            try {
                toBuy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //买票方法;
    private void toBuy() throws InterruptedException {
        //判断是否还有票;
        if(ticket<=0){
            flag=false;
            return;
        }
        //设置线程休眠;
        Thread.sleep(100);
        //输出买票信息;
        System.out.println(Thread.currentThread().getName()+"买到了票=>"+ticket--);
    }
}

在这里插入图片描述


(2)不安全的银行取钱案例

出现取款错误问题

public class NotSafeDemo02 {
    public static void main(String[] args) {
        //设置账户;
        Account account=new Account(200,"存款");
        //创建取钱线程;
        ByBank bb1=new ByBank(account,150,"阿猫");
        ByBank bb2=new ByBank(account,150,"阿狗");
        //启动线程;
        bb1.start();
        bb2.start();
    }
}
//账户类;
class Account {
    //定义存款和存款卡名;
     int money;
     String name;

    public  Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}
//银行类;
class ByBank extends Thread{
    //定义账户;
    Account account;
    //定义取款;
    int getMoney;
    //定义手中的钱数;
    int nowMoney;
    //构造方法;
    public ByBank(Account account, int getMoney, String name) {
        super(name);
        this.account = account;
        this.getMoney = getMoney;
    }
    //取钱;
    @Override
    public void run() {
        //判断是否还有钱;
        if(account.money-getMoney<0){
            System.out.println(Thread.currentThread().getName()+"钱不够用,取不了");
            return;
        }
        try {
            //设置线程休眠;
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //卡余额:存款-取款;
        account.money=account.money-getMoney;
        //现在手里的钱;
        nowMoney=nowMoney+getMoney;

        System.out.println(account.name+"余额==>"+account.money);
        //this.getName()也就是当前线程名 Thread.currentThread().getName()
        System.out.println(this.getName()+"手里的钱=>"+nowMoney);
    }
}

在这里插入图片描述


(3)线程不安全的集合

public class NotSafeDemo03 {
    public static void main(String[] args) {
        //新建集合;
        ArrayList<String> arrayList=new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            //创建线程->启动线程;
            new Thread(()->{
                //向集合添加元素;==>线程名
                arrayList.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            //设置线程休眠;
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("集合元素数量=>"+arrayList.size());
    }
}

由于线程不安全,可能会出现元素添加实际个数错误;

在这里插入图片描述


同步方法以及同步块

同步方法

由于可使用private关键字保证数据对象只能被方法访问,那么就需要针对方法使用synchronized关键字-同步机制;包括同步方法和同步块

隐式的锁

同步方法案例:
public synchronized void method( .参数/无参…){ .方法体…}

同步方法控制对对象的范围,而每个对象对应一把锁 , 每个同步方法都必须获得当前调用方法的对象的锁才能执行;否则线程会被阻塞;方法一旦执行时,就会独占这把锁,直到该方法返回才能释放锁,后面被阻塞的线程获得这个锁后才能执行;
但是同步方法有 缺点–>若将一个较复杂的方法设置为同步方法会影响效率;

若使用了过多的锁,可能会浪费资源


同步块

案例: synchronized(对象){…代码体.}

对象:同步监视器
这里括号中放入的对象可以是任何对象;但是一般为共享资源(或者说是用来增删改查的对象);
同步方法中无需指定同步监视器,由于同步方法中的同步监视器就是this,即这个对象本身,或者class;

同步监视器的执行过程;

  • 1号线程访问,锁定同步监视器,执行代码;
  • 2号线程访问,但是同步监视器此时锁定,无法访问;
  • 1号线程访问完毕,解锁同步监视器;
  • 2号线程访问,锁定同步监视器,执行代码.

修改刚才的不安全线程买票案例
在买票方法处加上synchronized关键字,变为同步方法;

public class NotSafeDemo01 {
    public static void main(String[] args) {
        //创建买票类对象;
        BuyTicket by=new BuyTicket();
        //创建线程且启动;
        new Thread(by,"阿智").start();
        new Thread(by,"阿猫").start();
        new Thread(by,"阿狗").start();
    }
}

//买票类;
class BuyTicket implements Runnable{
    //定义票的数量;
    private int ticket=10;
    //定义的标志位;
    boolean flag=true;
    //重写的run方法;
    @Override
    public void run() {
        //买票;
        while (flag){
            try {
                toBuy();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    //买票方法;
    //使用synchronized关键字,变为同步方法;锁的是this;
    private synchronized void toBuy() throws InterruptedException {
        //判断是否还有票;
        if(ticket<=0){
            flag=false;
            return;
        }
        //设置线程休眠;
        Thread.sleep(100);
        //输出买票信息;
        System.out.println(Thread.currentThread().getName()+"买到了票=>"+ticket--);
    }
}

买票过程井然有序

在这里插入图片描述


修改刚才的不安全线程取款案例

注意要把共享资源账户作为同步监视器,用同步代码块将所有的取款操作锁起来;

public class NotSafeDemo02 {
    public static void main(String[] args) {
        //设置账户;
        Account account = new Account(200, "存款");
        //创建取钱线程;
        ByBank bb1 = new ByBank(account, 150, "阿猫");
        ByBank bb2 = new ByBank(account, 150, "阿狗");
        //启动线程;
        bb1.start();
        bb2.start();
    }
}

//账户类;
class Account {
    //定义存款和存款卡名;
    int money;
    String name;

    public Account(int money, String name) {
        this.money = money;
        this.name = name;
    }
}

//银行类;
class ByBank extends Thread {
    //定义账户;
    Account account;
    //定义取款;
    int getMoney;
    //定义手中的钱数;
    int nowMoney;

    //构造方法;
    public ByBank(Account account, int getMoney, String name) {
        super(name);
        this.account = account;
        this.getMoney = getMoney;
    }

    //取钱;
    @Override
    public void run() {

        //注意将共享资源账户:account;作为同步监视器;
        synchronized (account) {
            //判断是否还有钱;
            if (account.money - getMoney < 0) {
                System.out.println(Thread.currentThread().getName() + "钱不够用,取不了");
                return;
            }
            try {
                //设置线程休眠;
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //卡余额:存款-取款;
            account.money = account.money - getMoney;
            //现在手里的钱;
            nowMoney = nowMoney + getMoney;

            System.out.println(account.name + "余额==>" + account.money);
            //this.getName()也就是当前线程名 Thread.currentThread().getName()
            System.out.println(this.getName() + "手里的钱=>" + nowMoney);
        }

    }
}

取钱过程井然有序

在这里插入图片描述


修改刚才的不安全线程的集合案例
将集合对象作为同步监视器,用同步代码块将添加方法包裹起来;

public class NotSafeDemo03 {
    public static void main(String[] args) {
        //新建集合;
        ArrayList<String> arrayList=new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            //创建线程->启动线程;
            new Thread(()->{
                //向集合添加元素;==>线程名
                //将集合对象作为同步监视器;
                synchronized (arrayList){
                arrayList.add(Thread.currentThread().getName());
                }
            }).start();
        }
        try {
            //设置线程休眠;
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("集合元素数量=>"+arrayList.size());
    }
}

所有元素已正常添加

在这里插入图片描述


CopyOnWriteArrayList(线程安全的集合之一)
public class Demo {
    public static void main(String[] args) {
        //创建集合;
        CopyOnWriteArrayList cwa=new CopyOnWriteArrayList();
        for (int i = 0; i < 10000; i++) {
            new Thread(()->{
                cwa.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            //线程休眠设置;
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("集合元素个数->"+cwa.size());
    }
}

不使用同步关键字,本身就是线程安全的集合;
在这里插入图片描述


8.死锁

多个线程各自占有一些共享资源,并且相互等待其他线程占有的资源才能运行,而导致的两个或多个线程都在等待对方释放资源,都停止执行了;
一个同步块同时拥有 两个以上对象的锁,可能发生死锁

产生死锁的四个条件:

  • 互斥:一个资源每次只能被一个进程使用;
  • 请求与保持条件:一个进程由于请求资源导致阻塞,对于已经获取的资源保持不放;
  • 不剥夺条件;进程已获得的资源,在未使用完之前,不能强行剥夺;
  • 循环等待条件;若干进程之间形成一种头尾相接的循环等待资源关系

死锁案例:

public class DeadLock {
    public static void main(String[] args) {
        //创建线程;
        MakeUp mu1=new MakeUp("甲",0);
        MakeUp mu2=new MakeUp("乙",1);
        //启动线程;
        mu1.start();
        mu2.start();
    }
}

//口红类;
class Lipstic{ }
//镜子类
class Mirror{ }
//化妆类;
class MakeUp extends Thread {
    //共享资源;
    static Lipstic lipstic = new Lipstic();
    static Mirror mirror = new Mirror();

    //使用者;
    String name;
    //选择;
    int choice;
    //构造方法;
    MakeUp(String name, int choice) {
        this.name = name;
        this.choice = choice;
    }

    //重写run方法;
    @Override
    public void run() {
        try {
            //化妆方法;
            makeMeth();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //化妆方法;
    public void makeMeth() throws InterruptedException {
        if(choice==0){
            //获得口红的锁;
            synchronized (lipstic){
                System.out.println(this.name+"获得口红锁");
                //线程设置休眠;
                Thread.sleep(1000);
            //获得镜子的锁;
            synchronized (mirror){
                System.out.println(this.name+"获得镜子锁");
            }
          }
        }else {
            //获得口红的锁;
            synchronized (mirror){
                System.out.println(this.name+"获得镜子锁");
                //线程设置休眠;
                Thread.sleep(2000);
                //获得镜子的锁;
                synchronized (lipstic){
                    System.out.println(this.name+"获得口红锁");
                }
            }
        }
    }
}

两人都占有对方的锁

在这里插入图片描述


修改一下案例,不让同时占有两个锁;

public class DeadLock2 {
    public static void main(String[] args) {
        //创建线程;
        MakeUp mu1=new MakeUp("甲",0);
        MakeUp mu2=new MakeUp("乙",1);
        //启动线程;
        mu1.start();
        mu2.start();
    }
}

//口红类;
class Lipstic{ }

//镜子类
class Mirror{ }

//化妆类;
class MakeUp extends Thread {
    //共享资源;
    static Lipstic lipstic = new Lipstic();
    static Mirror mirror = new Mirror();

    //使用者;
    String name;
    //选择;
    int choice;
    //构造方法;
    MakeUp(String name, int choice) {
        this.name = name;
        this.choice = choice;
    }

    //重写run方法;
    @Override
    public void run() {
        try {
            //化妆方法;
            makeMeth();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //化妆方法;
    public void makeMeth() throws InterruptedException {
        if (choice == 0) {
            //获得口红的锁;
            synchronized (lipstic) {
                System.out.println(this.name + "获得口红锁");
                //线程设置休眠;
                Thread.sleep(1000);
            }
            //获得镜子的锁;
            synchronized (mirror) {
                System.out.println(this.name + "获得镜子锁");
            }
        } else {
            //获得口红的锁;
            synchronized (mirror) {
                System.out.println(this.name + "获得镜子锁");
                //线程设置休眠;
                Thread.sleep(2000);
            }
            //获得镜子的锁;
            synchronized (lipstic) {
                System.out.println(this.name + "获得口红锁");
            }
        }
    }
}

即可正常运行

在这里插入图片描述


9.Lock锁

从jdk5开始,java提供了显式定义同步锁的对象实现同步,使用Lock对象作为同步锁;

java.util.concurrent.locks.Lock接口控制多个线程对于共享资源进行访问;
锁提供了对于共享资源的独占访问,每次只有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得的Lock对象;
ReentrantLock(可重入锁)类实现了Lock,和synchronized一样,有并发性和内存语义;在实现线程安全的控制中,常用的是ReentrantLock,可显式加锁,释放锁.

一般将加锁lock()放入try{ }代码块中;解锁unlock放在finally{ }代码块中(即使出现异常,也能释放锁);


synchronized与Lock

(1)Lock是显式锁,手动开启关闭,而synchronized是隐式锁,出了作用域就自动释放锁;
(2)Lock只有代码块锁,而synchronized还有方法锁;
(3)使用Lock锁,JVM将花费较少的时间来调度线程,性能更好,扩展性好
(4)优先顺序:Lock > 同步代码块 >同步方法

练习;买票案例(加入Lock锁)

public class LockDemo {
    public static void main(String[] args) {
       //创建实现类对象
        BuyTicket by=new BuyTicket();
        //创建线程后启动;
        new Thread(by,"阿猫").start();
        new Thread(by,"阿狗").start();
    }
}
//买票
class BuyTicket implements Runnable{
    //定义票数;
    int ticket=10;
    //定义锁;
    private  final  ReentrantLock lock=new ReentrantLock();
    //重写的run方法;包含线程执行体;
    @Override
    public void run() {
        try{
            //加锁;
            lock.lock();
            while(true){
                if(ticket>0){
                    System.out.println(Thread.currentThread().getName()+"买票"+ticket--);
                    try {
                        //设置线程休眠
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }else {
                    break;
                }
            }
        }finally {
            //解锁:
            lock.unlock();
        }
    }
}

这里加锁后,不会出现两个人拿到同一张票.

在这里插入图片描述


10.生产者消费者问题

  • 假设仓库只能放一件产品,生产者可以将生产品放入仓库,消费者可以取出产品;
  • 假设仓库没货了,那么生产者就要补货;仓库的货一直没人取,那么生产者就会停止生产,进入等待;
  • 若仓库有货,消费者可以取货,若没有,那么消费者就停止消费,进入等待

分析:
该问题涉及到线程同步,生产者与消费者共享一个资源,且两者之间相互依赖,互为条件;
在该问题中,若只是使用synchronized还远远不够,

synchronized可阻止并发更新同一个共享资源,实现同步,但不能用来实现不同线程之间的消息通信

线程通信常用方法;
属于Object类方法,只能用在同步方法或同步代码块中,否则抛出异常IllegalMonitorStateException;

方法说明
wait( )表示线程一直等待,直到其他线程通知,与sleep方法不同的是,该方法可释放锁
wait(long time)设置等待的时间(毫秒)
notify()唤醒一个处于等待状态的线程
notifyAll()唤醒同一个对象上所有调用了wait方法的线程

解决方式1:管程法

引入一个缓冲区,这个缓冲区可以设定大小;
生产者将产品存入缓冲区,消费者在缓冲区取产品;

代码实现

public class DemoPC {
    //主方法;
    public static void main(String[] args) {
        //新建容器对象;
        SyncBuffer syncBuffer=new SyncBuffer();

        //创建线程,让生产者与消费者线程启动;
        new Producer(syncBuffer).start();
        new Consumer(syncBuffer).start();
    }
}
//生产者;
class  Producer extends Thread{
    //定义缓冲区作为属性;
    SyncBuffer syncBuffer;
    //构造方法;
    public Producer(SyncBuffer syncBuffer) {
        this.syncBuffer = syncBuffer;
    }
    //生产;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("生产编号为=>"+i+"的鸡----->");
            syncBuffer.push(new Chicken(i));
        }
    }
}
//消费者
class  Consumer extends Thread{
    //定义缓冲区作为属性;
    SyncBuffer syncBuffer;
    //构造方法;
    public Consumer(SyncBuffer syncBuffer){
        this.syncBuffer = syncBuffer;
    }
    //消费;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("<----消费编号为=>"+syncBuffer.pop().id+"的鸡");
        }
    }
}
//产品
class Chicken{
    //产品编号;
    int id;
    //初始化构造方法;
    public Chicken(int id) {
        this.id = id;
    }

}
//缓冲区;作为容器
class SyncBuffer{
    //定义容量大小;
    Chicken[] chickens=new Chicken[10];
    //定义容器计数器;
    int count=0;

    //生产者放产品;
    public synchronized void push(Chicken chicken){
        //若容器满了就等待消费者消费;
        if(count==chickens.length){
            try {
                //生产者进入等待;
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //若容器不满,则继续生产;
        chickens[count]=chicken;
        count++;
        //通知消费者消费;
        this.notifyAll();
    }
    //消费者取产品;
    public synchronized Chicken pop(){
        //判断还有没有产品;
        if(count==0){
            try {
                //消费者进入等待;
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //正常消费;
        count--;
        Chicken chicken=chickens[count];
        //通知生产者生产;
        this.notifyAll();

        return chicken;
    }
}

在这里插入图片描述


解决方式2:信号灯法

设置一个标记(布尔值类型),在生产和消费时分别进行设置标记的值;

public class DemoPC02 {
    //主方法
    public static void main(String[] args) {
        //创建柜台对象;
        Counter counter=new Counter();
        //创建生产者,消费者线程,并且启动;
        new Producer(counter).start();
        new Consumer(counter).start();
    }
}
//生产者;
class Producer extends Thread{
    //定义柜台属性;
    Counter counter;
    //构造方法;
    public Producer(Counter counter) {
        this.counter = counter;
    }
    //在这里调用生产方法;
    @Override
    public void run() {
        for (int i = 1; i < 101; i++) {

            if(i%2==0){
                counter.shengChan("#白开水#编号"+i);
            }else {
               counter.shengChan("#可口可乐#编号"+i);
            }
        }
    }
}
//消费者
class Consumer extends Thread{
    //定义柜台属性;
    Counter counter;
    //构造方法;
    public Consumer(Counter counter) {
        this.counter = counter;
    }
    //在这里调用消费方法;
    @Override
    public void run() {
        for (int i = 1; i < 101; i++) {
            counter.xiaoFei();
        }
    }
}

//柜台
class Counter {
    //生产者生产,消费者等待;---> true;
    //消费者消费,生产者等待;---> false;

    //产品-->水;
    String Water;
    //设置的标志位;
    boolean flag=true;
    //生产水;
    public synchronized void shengChan(String water){
        //当标志位为false,就让生产者线程等待;
        if(!flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("生产了==>"+water);
        //通知消费者线程;(唤醒);
        this.notifyAll();
        //更新水;
        this.Water=water;
        //改变标志位;
        this.flag=!this.flag;
    }

    //消费;
    public  synchronized void xiaoFei(){
        //当标志位为true;就让消费者线程等待;
        if(flag){
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("<==消费:"+Water);
        //通知生产者线程;(唤醒);
        this.notifyAll();
        //改变标志位;
        this.flag=!this.flag;
    }
}

生产什么就消费什么

在这里插入图片描述


11.创建线程之使用线程池

将提前创建的多个线程,放入线程池中,使用时直接获取,用完再放回线程池;避免了频繁创建销毁,实现了重复利用;

提高了响应资源;减少创建新线程的时间;
降低资源消耗,重复利用线程池中的线程,不需要每次都创建;
便于线程管理;


corePoolSize:核心池的大小;
maximunPoolSize:最大线程数;
keepAliveTime:线程没有任务时,最多保持的时间就终止


从JDK5开始,API出现线程池: ExecutorService ,Executors;

  • ExecutorService;线程池接口,常用子类 ; ThreadPoolExecutor
    • void execute(Runnable cmd):执行任务/命令,无返回值,常用执行Runnable;
    • <T>Future<T> submit(Callable<T> task) ;执行任务,有返回值,常用执行Callable
  • Executors:工具类,作为线程池的工厂类;用户创建并返回不同类型的线程池

练习

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

public class MyThreadPool {
    public static void main(String[] args) {
        //newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
        ExecutorService executorService= Executors.newFixedThreadPool(15);
        //执行线程
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        executorService.execute(new MyThread());
        //关闭服务;
        executorService.shutdownNow();
    }
}

//自定义线程类;
class MyThread implements Runnable{
    @Override
    public void run() {
       //Thread.currentThread().getName():线程名;
       System.out.println(Thread.currentThread().getName());
    }
}

在这里插入图片描述


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小智RE0

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

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

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

打赏作者

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

抵扣说明:

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

余额充值