学习多线程

学习多线程

1.认识程序,进程,线程

程序

程序由指令和数据组成,是静态的

进程

启动一个程序,就启动了一个进程。

进程就是程序的一次执行过程。

进程是动态的。

线程

一个进程包含多个线程。例如:播放视频时,有动画,有声音,有弹幕……

2.如何创建线程

继承Thread类

三板斧:

  1. 继承Thread类
  2. 重写run方法
  3. 启动线程

实现Runnable接口

四板斧:

  1. 实现Runnable接口
  2. 重写run方法
  3. 将线程对象放入Thread对象
  4. 启动Thread对象的线程

实现Callable接口

实现Callable接口

3.静态代理模式

一个关于结婚的例子

You you = new You();
WeddingFirm wf = new WeddingFirm(you);
wf.marry();
//You类,WeddingFirm类都实现了Marry接口,也就都实现了marry方法

代入到线程

MyThread mt = new MyThread();
Thread t = new Thread(mt);
t.start();
//MyThread 类,Thread 类都实现了 Runnable 接口,也就都实现了 run 方法

4.lambda表达式

函数式接口

函数式接口

只包含一个抽象方法的接口。一个典型的函数式接口:Runnable接口

逐步简化

  1. 外部类
  2. 静态内部类
  3. 局部内部类
  4. 匿名内部类
  5. lambda表达式
    1. 去参数类型
    2. 去小括号
    3. 去大括号

lambda语法

(参数列表) -> {实现抽象方法的方法体}

参数列表

  • 有一个参数,不需要小括号
  • 没有参数,或有多个参数必须加小括号
  • 参数类型要省略就全省略。为什么可以省略?Java会去检查运行类型(即函数式接口)的方法。

方法体

  • 只有一条语句:无论有没有返回值都不需要大括号。如果写了return,就一定要加大括号。
  • 有多条语句:一定要加大括号

举例:

Thread thread = new Thread(() -> System.out.println("Hello World!")).start();

完整例子

例一

package thread.lambda;

/**
 * 学习并体验一步步将对象简化为lambda表达式的感觉
 */
public class Lambda01 {
    public static void main(String[] args) {
        //3.局部内部类
        class MyClass3 implements MyLambda {

            @Override
            public void studyLambda() {
                System.out.println("学习Lambda3,局部内部类");
            }
        }

        //4.匿名内部类
        MyLambda myLambda4 = new MyLambda() {
            @Override
            public void studyLambda() {
                System.out.println("学习Lambda4,匿名内部类");
            }
        };

        MyLambda myLambda1 = new MyClass();
        myLambda1.studyLambda();

        MyLambda myLambda2 = new MyClass1();
        myLambda2.studyLambda();

        MyLambda myLambda3 = new MyClass3();
        myLambda3.studyLambda();

        myLambda4.studyLambda();

        //5.Lambda表达式
        MyLambda myLambda5 = () -> {
            System.out.println("学习Lambda5,Lambda表达式");
        };
        myLambda5.studyLambda();
    }

    //2.静态内部类
    static class MyClass1 implements MyLambda {

        @Override
        public void studyLambda() {
            System.out.println("学习Lambda2,静态内部类");
        }
    }
}

interface MyLambda {
    void studyLambda();
}

//1.外部类
class MyClass implements MyLambda {
    @Override
    public void studyLambda() {
        System.out.println("学习Lambda1,外部类");
    }
}

例二

package thread.lambda;

/**
 * 学习并感受带参数的lambda表达式,并逐步简化lambda表达式
 */
public class Lambda02 {
    public static void main(String[] args) {
        //3.局部内部类
        class ClassB3 implements LambdaB {
            @Override
            public void studyLambda(int a) {
                System.out.println("学习Lambda3,局部内部类,a = " + a);
            }
        }

        LambdaB b = new ClassB();
        b.studyLambda(520);

        b = new ClassB2();
        b.studyLambda(521);

        b = new ClassB3();
        b.studyLambda(522);

        //4.匿名内部类
        b = new LambdaB() {
            @Override
            public void studyLambda(int a) {
                System.out.println("学习Lambda4,匿名内部类,a = " + a);
            }
        };
        b.studyLambda(250);

        //5.lambda
        b = (int a) -> {
            System.out.println("学习Lambda5,带参数类型的Lambda表达式,a = " + a);
        };
        b.studyLambda(251);

        //6.简化参数列表,去掉参数类型。要去参数类型,就全部去掉。例如:(a,int b)是错的
        b = (a) -> {
            System.out.println("学习Lambda6,去掉参数类型,a = " + a);
        };
        b.studyLambda(252);

        //7.简化参数列表,去掉小括号。只有一个参数,才能去掉小括号
        b = a -> {
            System.out.println("学习Lambda7,去掉小括号,a = " + a);
        };
        b.studyLambda(253);

        //8.简化参数列表,去掉大括号
        b = a -> System.out.println("学习Lambda8,去掉大括号,a = " + a);
        b.studyLambda(254);
    }

    //2.静态内部类
    static class ClassB2 implements LambdaB {

        @Override
        public void studyLambda(int a) {
            System.out.println("学习Lambda2,静态内部类,a = " + a);
        }
    }
}

interface LambdaB {
    void studyLambda(int a);
}

//1.外部类
class ClassB implements LambdaB {
    @Override
    public void studyLambda(int a) {
        System.out.println("学习Lambda1,外部类,a = " + a);
    }
}

new Thread(Runnable) 构造方法中可以放入一个对象,这个对象必须是实现了Runnable接口的对象

扩展:

new 代理类(实现了 任意函数式接口的 匿名内部类 对象)

5.线程状态

线程状态

线程状态图1

线程状态图2

线程方法

线程方法图

线程停止

推荐使用 设置标志位 的方式,如flag =true/false

线程停止

线程休眠

线程休眠

线程礼让

线程礼让

线程插队

线程插队

监测线程状态

监测线程状态

线程优先级

线程优先级

守护线程

守护线程

6.线程同步

同步机制

先了解一个名词:并发

并发

多个线程想要获得(或操作)某个对象(或者某个资源),这种情况就叫做并发。但是当他们同时操作这个对象时,会有异常发生,例如:抢票问题(某一张票),取钱问题(某一张银行卡)。

这时,就要想些办法,来避免这些异常发生,于是,线程同步机制就出现了。说白了,线程同步是一种办法(或机制),这种办法,就是为了解决并发的问题。

于是,解决并发的问题,就变成了如何实现线程同步的问题。

同时和同步的区别:

同时和同步在含义和用法上有明显的区别。

同时强调的是时间因素,指的是在同一时刻,两个或多个事件同时发生。例如,当我们说“同时出发”时,指的是在某一特定的时间点,所有人或物都开始行动。

同步则不同,它更多地强调的是事件之间的协调性和一致性,而非单纯的时间因素。例如,在舞蹈表演中,每个舞者需要按照同样的节拍和步伐来表演,这就是同步。也就是说,所有舞者需要在时间和动作上保持一致,才能达到同步的效果。

以上是同时和同步的主要区别,希望对你有所帮助。

上面的解释来自于百度文心一言。

线程同步

队列和锁

光有队列,不一定安全。正在打饭的人得到一个锁,才能打饭,打完了饭,就释放掉锁,后面的人继续获取锁,继续打饭……

如果没有锁,前面的人还没打完饭,后面的人就迫不及待地也要打饭,于是,可能就打起来了……所以,队列加锁很有必要。

队列和锁

队列和锁解决了线程同步的安全性

同步锁

synchronized 同步锁

同步锁

同步方法

同步方法

同步方法弊端

同步方法弊端

同步块

同步块

同步代码块的优势就在于:想锁哪里就锁哪里,想锁谁就锁谁。不需要像方法那样,把一整个方法体都锁住。

死锁

死锁代码

释放死锁:

释放死锁

死锁概念:

死锁概念

避免死锁:

避免死锁

Lock锁

Lock锁的概念

Lock锁的概念

自定义lock锁

自定义lock锁

synchronized与lock对比

synchronized与lock对比

7.线程通信

生产者与消费者模式

生产者与消费者模式

线程通信问题分析

线程通信问题分析

解决线程通信的方法

解决线程通信的方法

模型1:

模型1

管程法代码实现

package thread.communication;

/**
 * 测试生产者与消费者模式:利用缓冲区解决(管程法)
 * 需要生产者,消费者,产品,缓冲区
 */
public class TestPC {
    public static void main(String[] args) {
        SynContainer synContainer = new SynContainer();
        new Producer(synContainer).start();
        new Consumer(synContainer).start();
    }
}

//消费者
class Consumer extends Thread {
    SynContainer synContainer;

    public Consumer(SynContainer synContainer) {
        this.synContainer = synContainer;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println("消费了第" + synContainer.pop().id + "只鸡");
        }
    }
}

//生产者,相当于后厨
class Producer extends Thread {
    SynContainer synContainer;

    public Producer(SynContainer synContainer) {
        this.synContainer = synContainer;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            synContainer.push(new Chicken(i));
            System.out.println("生产了第" + i + "只鸡");
        }
    }
}

//容器,相当于前台
class SynContainer {
    //定义数组,存放产品
    Chicken[] chickens = new Chicken[10];
    //计数器
    int cnt = 0;

    //生产者放入产品
    public synchronized void push(Chicken chicken) {
        //如果满了,就等待消费者消费
        if (10 == cnt) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        //如果没满,就放入产品
        chickens[cnt++] = chicken;
        //通知消费者可以消费
        this.notifyAll();
    }

    //消费者取出产品
    public synchronized Chicken pop() {
        //如果容器为空,就等待生产者生产
        if (0 == cnt) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //通知生产者可以生产
        this.notifyAll();
        //如果没空,就取出产品
        return chickens[--cnt];
    }
}

class Chicken {
    public int id;

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

模型2:

模型2

信号灯法代码实现

package thread.communication;

/**
 * 生产者消费者问题2:信号灯法,标志位解决
 */
public class TestPC2 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Actor(tv).start();
        new Watcher(tv).start();
    }
}

//生产者:演员
class Actor extends Thread {
    TV tv = new TV();

    public Actor(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("抖音");
        }
    }
}

//消费者:观众
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;//false:观众正在观看,演员不需要表演;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;
    }
}

注意点:线程间通信的前提是 线程同步,所以依然需要设置锁。

8.线程池

线程池概念

线程池概念

使用线程池

使用线程池

线程池代码实现

package thread.pool;

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.shutdown();
    }
}

class MyThread implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}
posted @ 2023-09-16 13:56  yx1024  阅读(67)  评论(0编辑  收藏  举报