java 线程 lambda 表达式 案例及静态代理

线程:

如何使用线程方式:

1、实现Thread类(不推荐使用,避免了OOP的单继承局限性)

2、继承Runnable接口(推荐使用,避免了OOP的单继承局限性,灵活方便,方便一个对象多个线程使用)

3、线程池

4、Collxx接口(了解)

 

线程真正启动时通过start()方法

线程的run()方法中执行的是线程体

 

停止线程案例:

定义一个停止线程的方法 公开调用使得线程停止。

package com.stylefeng.guns.test;

/**
 * @author huanghao
 * @version 1.0
 * @create 2021/12/6 17:21
 */
public class TestStop  implements  Runnable{
    Boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println("run ...Thread "+i++);
        }
    }

    //设置一个公开的方法停止现场,转换标志位
    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("线程停止了");
            }
        }

    }
}

  

龟兔赛跑案例:

 

静态代理代码案例:

 1 package com.stylefeng.guns.test;
 2 
 3 /**
 4  * @author huanghao
 5  * @version 1.0
 6  * @create 2021/12/2 9:23
 7  */
 8 //静态代理:
 9     //静态代理中真实对象和代理对象都要实现同一个接口
10     //真实对象中做自己独立的一件事
11     //代理对象中可以做其他的是
12     //使用代理对象代理真实对象(即向代理对象中注入真实对象的实例,让真实对象去执行真实对象中的方法,然后代理对象又可以做自己的其他事)
13     //示例:结婚的婚庆公司  代理:婚庆公司  真实对象:结婚的人  婚庆公司布置 真正结婚的事情真实对象做

//真实对象和代理对象分别实现同一个接口
//在代理类中使用构造方法注入接口类型的对象,他可以向下转型为真实对象,因为真实对象实现了这个接口
//所以在new 代理对象的时候其构造方法使用的是真实对象 然后代理new 出来的对象就是指向真实对象
//调用实现接口中的方法就是实现的是真实对象中的方法
//在代理对象实现接口中的方法中调用真实对象实现接口的方法 在new的时候 接口向下转型指向真实对象,调用接口中的方法就是真实对象中的方法
//这就是所谓的静态代理。
14 15 public class StaticProxy { 16 public static void main(String[] args) { 17 // You you = new You(); 18 // WeddingMarryCompany weddingMarryCompany = new WeddingMarryCompany(you); 19 20 WeddingMarryCompany weddingMarryCompany = new WeddingMarryCompany(new You()); 21 //这里调用的方法就是真实对象中的方法只不过在代理对象中注入了真实对象,他们共同实现同一个接口 22 weddingMarryCompany.happyMarry(); 23 24 } 25 26 //接口 27 interface Marry{ 28 void happyMarry(); 29 } 30 31 //真实对象 32 static class You implements Marry{ 33 @Override 34 public void happyMarry() { 35 System.out.println("我自己结婚--->目标对象"); 36 } 37 } 38 39 //代理对象,和真实对象一样实现同一个接口 40 // 代理真实对象结婚 可以做其他的事,把真实对象注入进来 41 static class WeddingMarryCompany implements Marry{ 42 43 //注入接口类型的对象 44 private Marry target; 45 46 //代理对象的构造方法 注入真实对象 You 47 public WeddingMarryCompany (Marry target){ 48 this.target = target; 49 } 50 51 //重写的接口的方法 52 @Override 53 public void happyMarry() { 54 //代理对象自己做的事 55 before(); 56 //真实对象做的事 结婚 57 this.target.happyMarry(); 58 //代理对象自己做的事 59 after(); 60 } 61 } 62 63 private static void after() { 64 System.out.println("结婚之后收尾款"); 65 } 66 67 private static void before() { 68 System.out.println("结婚之前布置现场"); 69 } 70 71 72 }

 

 lambda表达式推导案例:

lambda表达式的使用场景在函数式接口中。

函数式接口:就是有且仅有一个抽象方法,但可以有多个非抽象方法的接口

函数式接口可以被隐式的转换为lambda表达式。

代码推导示例:

package com.stylefeng.guns.test;

import com.alibaba.druid.wall.violation.IllegalSQLObjectViolation;

/**
 * @author huanghao
 * @version 1.0
 * @create 2021/12/6 16:39
 */
public class Lambda {

    public static void main(String[] args) {

        //第一版: 单独定义函数式接口及实现类
        //调用
        Ilike like = new Like();
        like.lambda();


        //第二版:局部内部类
        class Like3 implements  Ilike{

            @Override
            public void lambda() {
                System.out.println("I like lambda3 !");
            }
        }
        //调用
        like = new Like3();
        like.lambda();

        //第三版: 匿名内部类
        like = new Ilike() {
            @Override
            public void lambda() {
                System.out.println("I like lambda4 !");
            }
        };
        //调用
        like.lambda();

        //第四版 在第三版的基础上 使用lambda 简化
        like = () ->{
            System.out.println("I like lambda5 !");
        };
        //调用
        like.lambda();


        //传入参数使用lambda: 简化1: lambda 表达式
        Ilove love = (int a) ->{
            System.out.println("i love you -->"+a);
        };

        //简化2:参数类型
        love  = (a) ->{
            System.out.println("i love you -->"+a);
        };

        //简化3:简化括号
        love = a -> {
            System.out.println("i love you -->"+a);
        };

        love.love(521);

        //两个参数 的 lambda表达式的简化
        Ilove2 love2 = (int a,int b) ->{
            System.out.println("i love you -->"+a);
            System.out.println("i love you -->"+b);
        };

        //两个参数简化括号
        love2 = (a,b) ->{
            System.out.println("i love you -->"+a);
            System.out.println("i love you -->"+b);
        };

        love2.love2(520,502);
        //总结:
        /**
         *   lambda 表达式只能有一行大爱吗的情况下才能简化为一行,如果有多行,那么就用代码块
         *   前提是接口为函数式接口
         *   多个参数也可以去掉参数类型,要去除就都去除,必须加上括号
         */

    }
}


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

interface  Ilove{
    void love(int a);
}

interface  Ilove2{
    void love2(int a,int b);
}

//实现类
class Like implements Ilike{

    @Override
    public void lambda() {
        System.out.println("I like lambda !");
    }
}

  

 并发:

同一个对象被多个线程同时操作

如: 1000个人抢一张票、两个银行同时取钱

碰到这种并发的问题:

我们就可以使用线程的同步

线程同步:

1、现实生活中,我们会遇到"同一个资源,多个人都想使用"的问题,比如,食堂排队打饭,每个人都想吃饭,解决办法就是,排队,一个个进行。

2、处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象,这时候我们就需要线程同步,

  线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个 对象的等待池 形成队列 ,等待前面线程使用完毕,下一个线程再使用。

所以线程同步机制:就是为了解决安全问题,防止数据紊乱

其形成的条件: 队列  + 锁 

java中的每一个对象都有一个锁

 

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题,为了保证数据在方法中被访问是的正确性,在访问时假如 锁机制 synchronized ,

当一个线程获得对象的排它锁,独占资源,其他线程必须等待,使用后释放锁即可,

弊端:

1、一个线程持有锁会导致其他所有需要此锁的线程挂起;

2、在多线程竞争下,枷锁,释放锁会导致较多的上下文切换和调度延时,引起性能问题;

3、如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。

 

线程同步其实就是一种等待机制我们可以使用 synchronized 关键字给方法加锁,或者使用同步代码块  java中有个线程安全的集合也可以使用

 

线程同步 是造成死锁的案例:

package com.stylefeng.guns.test;

/**
* @author huanghao
* @version 1.0
* @create 2021/12/8 13:43
*/

/**
* 多个线程占用同一个资源
* 出现并发
* 使用线程同步 synchronized
* 同步的原理 队列和锁
* 在线程同步的过程中一个线程占这另外一个线程的锁不进行释放
* 出现死锁
*/
//模拟死锁 化妆
//两个线程同时使用 Synchronized 锁资源 进行线程的同步
//在 Synchronized 中套 Synchronized 造成线程获取到另一个线程的资源不进行释放 导致产生死锁
//所以 这个Synchronized 需要分开使用 不能套用
//这个 Synchronized 可以使用在方法上 也可以使用在代码块上
//在使用的过程中 这个 Synchronized 的() 中定义的是正在使用的量 或者变化的量
//我们使用的集合List 也是线程不安全的 但是有一个集合类是线程安全的 我们可以直接拿来使用,而不需要使用 Synchronized
//除了使用 Synchronized 进行线程的同步 ,我们还可以使用 Lock 锁来进行资源的同步 但是我们要进行关锁和开锁的操作
//然后这个Lock 要放在 try catch 中在 finally 中释放锁
public class DeadLock {

public static void main(String[] args) {
MakeUp g1 = new MakeUp(0,"灰姑娘");
MakeUp g2 = new MakeUp(1,"白雪公主");

g1.start();
g2.start();

}
}

//口红
class Lipstick{

}

//镜子
class Mirror{

}

//化妆
class MakeUp extends Thread {

//需要的资源只有一份,用static保持一份
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();

int choice;//选择
String girlName;//化妆的人

//有参构造
MakeUp(int choice, String girlName) {
this.choice = choice;
this.girlName = girlName;
}

public void run() {
try {
makeUp();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

//化妆 就是互相持有对方的锁,需要拿到对方的资源
private void makeUp() throws InterruptedException {
if (choice == 0) {
/**Syncnronized中套用Syncnronized 造成死锁
Syncnronized(lipstick){
System.out.println(this.girlName+"获得了口红的锁");
Thread.sleep(1000);

Syncnronized(lipstick){
System.out.println(this.girlName+"获得了镜子的锁");
}
}
**/

// Syncnronized(lipstick){ //获得了口红的锁
// System.out.println(this.girlName+"获得了口红的锁");
// Thread.sleep(1000);
// }
//
// Syncnronized(lipstick){ //获得了镜子的锁
// System.out.println(this.girlName+"获得了镜子的锁");
// }
// }else {
// Syncnronized(lipstick){ //获得了口红的锁
// System.out.println(this.girlName+"获得了口红的锁");
// Thread.sleep(1000);
// }
//
// Syncnronized(lipstick){//获得了镜子的锁
// System.out.println(this.girlName+"获得了镜子的锁");
// }
// }
// }


}

}

}

  

Synchronized 和 Lock :
1、Lock 是显式锁(手动开启和关闭锁,在finally中关闭锁),synchhronized 是隐式锁,出了作用于自动释放

2、Lock只有代码块锁,synchronized有代码块锁和方法锁

3、使用Lock锁,jvm将花费较少的时间来调度线程,性能更好,并且具有更好的扩展性(提供了更多的子类)

4、使用先后顺序

Lock > 同步代码块 (已经进入了方法,分配相应的资源) > 同步方法(在方法提外)

 

 

两个线程使用了 Synchronized 后实现了同步 并且加锁后 两个线程就不会相互产生影响了

那么如果两个线程之间需要进行通信的话 我们可以采取管程法或者信号灯法实现两个线程之间的通信。

 

管程法使用场景: 在肯德基店内 我们点餐 前台给我们准备食物 后台的大厨给我们制作 前台就相当于一个缓冲区

后台大厨给我们准备了食物放在前台 没有了通知后台做 有通知顾客拿 

 

管程法代码案例:

两组线程 一个后厨 ,一个顾客 一个缓冲区(前台)

线程中的构造方法中注入 缓冲区对象 然后在线程的run方法中就可以使用 缓冲区对象

缓冲区定义两个方法 push pop 

然后就可以在各自的线程中调用这两个方法

将产品注入缓冲区

使用计数 count 进行判断 

让两个线程进行睡眠和唤醒

main中定义的两个线程的start方法就会启动各自的run方法 run方法中调用缓冲区对象的push pop 方法

使用计数 和产品注入缓冲区对线程进行睡眠和唤醒操作。

 

package com.stylefeng.guns.test;

/**
 * @author huanghao
 * @version 1.0
 * @create 2021/12/9 11:42
 */

//生产者消费者模型 --> 利用缓冲区解决: 管程法
//生产者,消费者, 产品,缓冲区

public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}


//生产者
class Productor extends  Thread{
    SynContainer container;

    public Productor (SynContainer container){
        this.container = container;
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; 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 < 100; i++) {
            System.out.println("消费了-->"+container.pop().id+"只鸡");
        }
    }

}

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

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

}

//缓冲区
class SynContainer{
    //需要一个容器大小
    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;
    }

}

  

信号灯法代码案例:

两组线程 一个演员 一个观众 TV 为表演的节目  TV 中有paly方法表演节目 有watch方法看节目

演员和观众的线程中的构造方法中注入 TV 对象  这样在new 演员和观众的时候 调用有参构造 得到了TV对象

然后在调用线程start方法的时候 调用了run方法 在run方法中 使用TV对象调用TV中的 paly方法和watch方法

在这个过程中定义flag 控制线程睡眠和唤醒 

package com.stylefeng.guns.test;

/**
 * @author huanghao
 * @version 1.0
 * @create 2021/12/9 14:14
 */
//这个版本使用信号灯法来使得两个线程之间进行通信
public class TestPC2 {
    public static void main(String[] args) {
        TV  tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }

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

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

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

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

    //产品-->节目
    static class TV{
        //演员表演,观众等待 T
        //观众观看,演员等待 F
        String voice;
        boolean flag = true;

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


        //观看
        public synchronized void watch(){
            if(flag){
                try {
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("观看了:"+voice);
            //通知演员表演
            this.notifyAll();
            this.flag = !this.flag;
        }
    }


}

  

 

posted @ 2021-12-02 09:16  易言。  阅读(101)  评论(0)    收藏  举报