Java-多线程(二)-静态代理、Lambda表达式、线程状态、线程方法

Java-多线程-2

学习视频:B站 狂神说Java – https://www.bilibili.com/video/BV1V4411p7EF

学习博客:csdn – https://blog.csdn.net/yalu_123456/article/details/91049333?spm=1001.2014.3001.5501

4、静态代理模式

静态代理模式总结

  • 真实对象和代理对象都要实现同一个接口
  • 代理对象要代理真实角色

好处:

  • 代理对象可以做很多真实对象做不了的事情
  • 真实对象专注做自己的事情

静态代理模式中,真实对象和代理对象都要实现同一个接口。 在这里的例子中 都去实现接口中的 happyMarry()方法。

那么需要对 真实对象实现接口类, 重写其中的方法

代理对象实现接口类, 在这其中它的属性需要有你这个真实对象,然后才能代理; 在实现接口的方法时, 它不仅仅能够帮你实现 你要做的事情,即this.you.happyMarry(); //你要结婚; 还能增加 附带 一些真实对象不能做的事情。这里的是 before() 和 after() 方法 事情。

然后这两个实现接口类, 代理对象和真实对象 在代理模式中创建对象,完成具体的静态代理。

例子:

package com.AL.Multithread;

//静态代理模式总结
//        真实对象和代理对象都要实现同一个接口
//        代理对象要代理真实角色
// 好处:
//        代理对象可以做很多真实对象做不了的事情
//        真实对象专注做自己的事情
public class StaticProxy {
    public static void main(String[] args) {
        // 代理对象   代理 真实的对象
        You1 you1 = new You1();
        you1.happyMarry();
        new WeddingCompany(you1).happyMarry();
    }
}

// 定义共同的接口: 结婚
// 真实对象和代理对象都要实现同一个接口, 你和婚庆公司都要实现结婚
interface Marry{
    void happyMarry();
}

// 真实对象: 你   你要实现结婚 接口
class You1 implements Marry{
    @Override
    public void happyMarry() {
        System.out.println("我要结婚了, 我好慌!!!");
    }
}

//代理对象:婚庆公司
class WeddingCompany implements Marry{
    //婚庆公司需要有你这个人。   代理对象需要有一个真实的对象
    private You1 you;

    public WeddingCompany(You1 you) {
        this.you = you;
    }
    @Override
    public void happyMarry() {
        before();
        this.you.happyMarry(); //你要结婚
        after();
    }
    private void before() {
        System.out.println("结婚之前,布置酒席");
    }
    private void after() {
        System.out.println("结婚之后,催你收钱");
    }
}

结果:

我要结婚了, 我好慌!!!
结婚之前,布置酒席
我要结婚了, 我好慌!!!
结婚之后,催你收钱

5、Lambda表达式

Lambda表达式

  1. 避免匿名内部类定义过多
  2. 其实质属于函数式编程的概念
  3. 可以让代码看起来很简洁
  4. 去掉了一堆没有意义的代码,只留下核心的逻辑

函数式接口

Functional Interface(函数式接口)

定义:

  • 任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
  • 对于函数式接口,可以通过lambda表达式来创建该接口的对象

5.1、Lambda表达式的推导

只有对于函数式接口,即只有一个方法的接口, 才能使用 Lambda表达式 去创建接口的对象。

例子1:

package com.AL.Multithread;

/**
 * 推导 Lambda 表达式
 */
public class TestLambda1 {
    public static void main(String[] args){
        ILike like = new Like(); //这个新建的对象,函数式接口,且继承了类 Like
        like.lambda(); //Like中的lambda 方法

        ILike like2 = new Like2();
        like2.lambda();

        //4. 局部内部类
        class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("我们的恋爱是对生命最大的浪费!");
            }
        }
        ILike like3 = new Like3();
        like3.lambda();

        // 5. 匿名内部类。  没有类的名称, 必须要借助接口或父类
        ILike like4 = new ILike(){
            @Override
            public void lambda() {
                System.out.println("我们结婚吧!!!");
            }
        };
        like4.lambda();

        // 6. 用lambda简化
        like = ()->{
            System.out.println("都是你的错!!!");
        };
        like.lambda();
    }
    // 3.静态局部类.  创建的是一个静态的局部 类 class
    static class Like2 implements ILike{
        @Override
        public void lambda() {
            System.out.println("I like fly");
        }
    }
}

// 1.定义一个函数式接口.
// 对于函数式接口,可以通过lambda表达式去创建这个接口的对象
interface ILike{
    void lambda();
}

// 2.实现类
class Like implements ILike{
    @Override
    public void lambda() { //这个方法的快捷键 ALT+INSERT
        System.out.println("I like lambda");
    }
}

结果:

I like lambda
I like fly
我们的恋爱是对生命最大的浪费!
我们结婚吧!!!
都是你的错!!!

例子2:

package com.AL.Multithread;

public class TestLambda2 {
    public static void main(String[] args){
        ILove love = new Love();
        love.love(1);
        ILove love2 =new Love2();
        love2.love(2);

        //局部内部类
        class Love3 implements ILove{
            @Override
            public void love(int a) {
                System.out.println("不爱我就拉倒--"+a);
            }
        }
        ILove love3 = new Love3();
        love3.love(3);

        //匿名内部类
        ILove love4 = new ILove(){
            @Override
            public void love(int a) {
                System.out.println("放学别走啊--"+a);
            }
        };
        love4.love(4);

        //lambda 表达式
        ILove love5 = (int a)->{
            System.out.println("give me an mojito!!-->"+a);
        };
        love5.love(5);
    }
    //静态局部类
    static class Love2 implements ILove{
        @Override
        public void love(int a) {
            System.out.println("我爱你吗?-->"+a);
        }
    }

}

//函数式接口。  只有一个抽象方法
interface ILove{
    void love(int a);
}
//实现类
class Love implements ILove{
    @Override
    public void love(int a) {
        System.out.println("I love you -->"+a);
    }
}

结果:

I love you -->1
我爱你吗?-->2
不爱我就拉倒--3
放学别走啊--4
give me an mojito!!-->5

对于具有多个参数类型的函数式接口:

package com.AL.Multithread;

public class TestLambda3 {
    public static void main(String[] args) {
        YouLove youLove = (a, b) -> {
            System.out.println("我说了:"+a+b);
        };
        youLove.youLove(3,5);
    }
}

interface YouLove {
    void youLove(int a, int b);
}

结果:

我说了:35

总结:

  1. lambda表达式只能有一行代码的情况下才能简化为一行,如果有多行,那么就用代码块包裹
  2. 前提是接口为函数式接口。即接口里面只有一个方法。
  3. 多个参数也可以去掉参数类型,要去掉就都去掉,必须加上括号

5.2、Lambda表达式在多线程中的应用

package com.AL.Multithread;

//lambda表达式在多线程中的使用
public class TestLambda4 {
    public static void main(String[] args) {

        //原先方式
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("你讲咩啊");
            }
        }).start();

        //线程体只有一行可以省略到极致
        new Thread(()-> System.out.println("你讲咩啊讲")).start();

        //如果线程体有多行 , 用一个代码块包裹起来就好.
        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                System.out.println("你说啥?");
            }
        }).start();
    }
}

6、线程状态

6.1、线程状态

  • 线程在创建 新建 new 的时候就进入到了新生状态
  • 在调用start()方法的时候,线程就立即进入了就绪状态。 但这时候需要等待cpu调度,才能去进行执行;
  • 当cpu对已经就绪状态的线程进行调度执行时,会进入运行状态
  • 进入运行状态时,线程才开始真正执行线程体的代码块
    • 当在运行状态时, 调用了 sleep,wait或同步锁定时,线程进入阻塞状态,这个时候,代码不会往下继续执行; 当阻塞状态解除后重新进入就绪状态,然后等待 CPU 的调度执行;
    • 当在运行状态,等待线程执行完毕, 就会进入死亡状态,无法再次启动。
      在这里插入图片描述
      在这里插入图片描述

线程方法

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

6.2、线程停止

停止线程

不推荐使用JDK提供的 stop()、destroy()方法。【已废弃】
推荐线程自己停止下来
建议使用一个标志位进行终止变量
当flag=false,则终止线程运行。

例子: 我们测试一个使线程正常停止的代码,在这里,我们利用一个标志位进行终止线程的运行

在这里我们重写了 run()方法, 然后调用 start()方法使线程进入就绪状态,然后等待 cpu调度执行。 此时多线程之间交替执行, 直到次数达到900 并改变相应的标志位, 去终止线程的运行。

package com.AL.Multithread;

// 测试 stop
// 1.建议线程正常停止---利用次数,不建议使用死循环
// 2.建议使用标志位--设置一个标志位,来决定stop
// 3.不要使用JDK中提供的 stop、destory方法,
public class TestStop implements Runnable{
    // 1.设置一个标志位 flag
    private Boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println("IG go"+ 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 < 1000; i++) {
            System.out.println("main"+ i);
            if (i==900){
                // 调用stop方法,改变标志位的值,让线程停止
                testStop.stop();
                System.out.println("IG 要输了");
            }
        }
    }
}

结果: 我们可以看到在调用 start()方法后, 多线程之间交替执行(表现在 run方法和输出的main在交替打印输出),在i==900的时候,调用了stop方法去改变标志位,令线程终止运行,最终线程停止。

main0
IG go0
main1
IG go1
main2
...
main900
IG 要输了
main901
...
main998
main999

6.3、线程休眠

线程休眠

sleep(时间)指定当前线程阻塞的毫秒数;

sleep存在异常InterruptedException

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

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

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

例子:模拟网络延时。

模拟网络延时的作用是 放大问题的发生性。 如放大多线程时的并发性。

sleep(time) 其中的表示当前线程阻塞的毫秒数, 我们模拟延时200毫秒。在这里,我们实现Runnable接口类去创建多线程, 重写 run()方法,每次买票时票数-1;调用 start()方法,线程进入就绪状态,等待cpu调度; 进入运行后,线程运行,等待sleep 休眠进入阻塞状态, 再次进入就绪状态, 运行, 最终死亡,线程结束。

package com.AL.Multithread;

// 模拟网络延时
public class TestSleep implements Runnable {
    // 票数
    private int ticketNums = 10;

    @Override
    public void run() {
        while (true){
            if (ticketNums <= 0){
                break;
            }
            // 添加模拟延时。   可以去放大 问题的发生性
            try {
                Thread.sleep(200); //200毫秒
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+ ticketNums--+"票");
        }
    }

    public static void main(String[] args){
        TestThread4 ticket = new TestThread4();

        new Thread(ticket,"小明").start();
        new Thread(ticket,"小红").start();
        new Thread(ticket,"黄牛党").start();
    }
}

结果: 模拟延时放大问题的发生性, 在这里多线程的并发性: 票数有重复的,数据紊乱。

黄牛党拿到了第8票
小红拿到了第10票
小明拿到了第9票
小明拿到了第7票
小红拿到了第5票
黄牛党拿到了第6票
小红拿到了第4票
黄牛党拿到了第3票
小明拿到了第4票
黄牛党拿到了第1票
小明拿到了第0票
小红拿到了第2票

模拟倒计时的例子: 倒计时1秒,打印输出系统当前时间。

package com.AL.Multithread;

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

// sleep() 模拟倒计时
public class TestSleep2 {
    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 (Exception e) {
                e.printStackTrace();
            }
        }
    }
    // 模拟倒计时
    public static void tenDown() throws InterruptedException{
        int num = 10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if(num<=0){
                break;
            }
        }
    }
}

结果:

21:53:43
21:53:44
21:53:45
21:53:46
21:53:47

7、线程方法

7.1、线程礼让

线程礼让:

  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • cpu重新调度,礼让不一定成功!看CPU心情。

测试线程礼让. 使用 Thread.yield()方法。例子:

package com.AL.Multithread;

// 测试线程礼让.  使用 Thread.yield()方法
// 礼让线程不一定成功, 看CPU的心情进行调度.
public class TestYield {
    public static void main(String[] args){
        MyYield myYield = new MyYield();

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

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

7.2、线程强制执行

join

join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞

可以想象成插队。

在进行 join 使线程强制执行时,即先将此线程,插队进去的执行完, 然后再执行其它线程。其它线程堵塞

例子:

package com.AL.Multithread;

//测试 join 方法, 可以想象成插队。
// 先将此线程,插队进去的执行完, 然后再执行其它线程。其它线程堵塞
public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("线程的爸爸来了"+i);
        }
    }
    public static void main(String[] args) throws InterruptedException{
        //启动我们的线程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        //主线程
        for (int i = 0; i < 1000; i++) {
            if (i==200){
                thread.join(); //插队了
            }
            System.out.println("main"+ i);
        }
    }
}

7.3、观测线程状态

Thread State: 5个状态,新生、就绪、运行(阻塞)、死亡。

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

new :尚未启动的线程处于此状态

Runnable :在java虚拟机中执行的线程处于此状态

Blocked :被阻塞等待监视器锁定的线程处于此状态。

Waiting :正在等待另一个线程执行特定动作的线程处于此状态。

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

Terminated :已退出的线程处于此状态。这个表示线程已经终止了

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

例子:

package com.AL.Multithread;

// 观察测试线程的状态
// 线程的5个状态: 新生、就绪、运行、阻塞、死亡
public class TestState {
    public static void main(String[] args) throws InterruptedException{
        Thread thread = new Thread(()->{
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.println("IG 给我冲啊!!!");
        });

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

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

        // Terminated 已退出的线程处于此状态
        while (state != Thread.State.TERMINATED){ //只要线程不终止,就一直输出状态
            Thread.sleep(100);
            state = thread.getState(); //更新线程状态
            System.out.println(state); //输出状态
        }
    }
}

结果:

NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
IG 给我冲啊!!!
TERMINATED

注意:线程中断或者结束的时候,一旦进入死亡状态,就不能再次启动。 线程只能运行一次

7.4、线程的优先级

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

先设定优先级,然后再启动

需要注意的是:优先级低也会存在着被调用,这主要是看CPU的调度

线程的优先级用数字表示,范围从1~10。

使用以下方式改变或获取优先级: getPriority(). setPriority(int xxx)

测试线程优先级的例子:

package com.AL.Multithread;

// 测试线程的优先级
// 需要注意的是:优先级低也会存在着被调用,这主要是看CPU的调度
public class TestPriority {
    public static void main(String[] args){
        // 主线程默认优先级
        System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());

        MyPriority myPriority = new MyPriority();
        Thread t1 = new Thread(myPriority);
        Thread t2 = new Thread(myPriority);
        Thread t3 = new Thread(myPriority);
        Thread t4 = new Thread(myPriority);
        Thread t5 = new Thread(myPriority);
        Thread t6 = new Thread(myPriority);

        // 先设置优先级, 再启动
        // 优先级的范围是: 1-10
        t1.start();

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

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

        t4.setPriority(Thread.MAX_PRIORITY); //MAX_PRIORITY=10
        t4.start();

        t5.setPriority(-1);
        t5.start();

        t6.setPriority(12);
        t6.start();

    }

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

结果:

main-->5
Thread-3-->10
Thread-0-->5
Thread-1-->1
Thread-2-->4
Exception in thread "main" java.lang.IllegalArgumentException

报错的原因是: 线程的优先级范围是 1- 10

修改后就可以正常运行了:

t5.setPriority(2);
t5.start();

t6.setPriority(10);
t6.start();

结果:

main-->5
Thread-3-->10
Thread-5-->10
Thread-4-->2
Thread-0-->5
Thread-2-->4
Thread-1-->1

7.5、守护线程

线程分为用户线程和守护线程

虚拟机必须确保用户线程执行完毕

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

如,后台记录操作日志,监控内存,垃圾回收等待。。。

不过守护线程结束的时候, 即虚拟机关闭 需要一定时间

测试守护规则。 Thread.setDaemon(true); 此方法中默认为 false 表示用户线程,正常的线程都是用户线程。、我们修改为 true 使对应的线程为守护线程。

例子:

package com.AL.Multithread;

// 测试守护线程
// 上帝守护你   虚拟机必须确保用户线程执行完毕,  虚拟机不用等待守护线程执行完毕
public class TestDaemon {
    public static void main(String[] args){
        God god = new God();
        You you = new You();

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

        thread.start();

        new Thread(you).start(); //你  用户线程启动
    }
}
// 上帝
class God implements Runnable{
    @Override
    public void run() {  //快捷方式 CTRL+I
        while (true){
            System.out.println("上帝永远守护你");
        }
    }
}
//你自己
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("你要当一辈子的废物,还是一秒钟的英雄");
        }
        System.out.println("======物资不行啊!!!========");
    }
}

部分结果: 虚拟机会确保用户线程执行完毕,即此时确保了 You 的Runnable接口实现类这个用户线程执行,直到for循环结束; 而不管守护线程运行是否结束。

因为这里默认守护线程应该是一直执行下去的,但是已经结束了, 这说明虚拟机不理会守护线程是否执行完毕,会自行在用户线程执行完毕后,自己关闭。

======物资不行啊!!!========
上帝永远守护你
上帝永远守护你
上帝永远守护你
上帝永远守护你
上帝永远守护你
上帝永远守护你
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多线程lambda表达式是两个不同的概念,但它们可以结合使用来实现多线程编程。 在Java中,可以使用lambda表达式来简化多线程编程。Lambda表达式可以将一个函数作为参数传递给另一个函数或方法,并且可以在需要函数式接口的地方使用。 在多线程编程中,通常使用java.util.concurrent包中的ExecutorService来创建线程池并管理线程的执行。可以使用lambda表达式来定义要执行的任务。 下面是一个示例代码,展示了如何使用lambda表达式创建一个多线程任务: ``` import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class MultiThreadLambdaExample { public static void main(String[] args) { // 创建一个包含两个线程线程池 ExecutorService executor = Executors.newFixedThreadPool(2); // 使用lambda表达式定义要执行的任务 Runnable task1 = () -> { System.out.println("Task 1 is running"); }; Runnable task2 = () -> { System.out.println("Task 2 is running"); }; // 提交任务给线程池执行 executor.submit(task1); executor.submit(task2); // 关闭线程池 executor.shutdown(); } } ``` 在上面的示例中,我们使用lambda表达式定义了两个任务(task1和task2),然后将它们提交给线程池执行。每个任务都会打印一条消息。 请注意,这只是一个简单的示例,实际的多线程应用可能涉及更加复杂的任务和线程管理。但是,lambda表达式可以帮助简化多线程编程,并提供更清晰和简洁的代码。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值