多线程基础

多线程基础

笔记总结自: 狂神说多线程 https://www.bilibili.com/video/BV1V4411p7EF?p=1

1. 基本概念

  • 进程:由操作系统分配的独立的内存空间(理解为一个应用的),由至少一个线程组成
  • 线程:进程中的一个执行任务,多个线程共享内存(多线程安全问题)

2. 创建线程

2.1 继承自Thread,重写run()方法

package thread;

/**
 * 创建线程方式一
 *
 * @author dingwen
 * @date 2021/08/30
 */
public class CreateThread1 extends Thread {
    @Override
    public void run() {
        // 获取当前线程名称
        System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
        System.out.println("创建了一个新的线程");
    }

    public static void main(String[] args) {
        // main 线程
        System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
        CreateThread1 thread1 = new CreateThread1();
        thread1.start();
    }
}

2.2 实现Runnable接口,重写run()

基于静态代理实现:通过构造器传入目标对象,再由代理对象执行相对应的方法进行调用。

package thread;

/**
 * 创建线程方式2
 *  静态代理方式实现
 *
 * @author dingwen
 * @date 2021/08/30
 */
public class CreateThread2 implements Runnable{
    @Override
    public void run() {
        System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
        System.out.println("创建了一个新的线程");
    }

    public static void main(String[] args) {
        System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
        System.out.println("main 线程");
        new Thread(new CreateThread2()).start();
    }
}

2.3 实现Callable接口,重写call()

package thread;

import java.util.concurrent.*;

/**
 * 创建线程方式3
 *
 * @author dingwen
 * @date 2021/08/30
 */
public class CreateThread3 implements Callable<Boolean> {
    @Override
    public Boolean call() throws Exception {
        System.out.println("新的线程创建成功");
        return true;
    }

    public static void main(String[] args){

        try {
            System.out.println("main 线程");
            System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
            CreateThread3 thread3 = new CreateThread3();
            // 创建执行线程服务
            // 指定线程池大小为1
            ExecutorService executorService = Executors.newFixedThreadPool(1);
            // 提交执行
            Future<Boolean> submit = executorService.submit(thread3);
            System.out.println("submit.get() = " + submit.get());
            // 关闭
            executorService.shutdownNow();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

3.案例

3.1 多线程

3.1.1 继承Thread实现
package thread;

/**
 * demo01
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class Demo01 extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("新线程执行第:" + i + "次");
        }
    }

    public static void main(String[] args) {
        new Demo01().start();
        for (int i = 0; i < 2000; i++) {
            System.out.println("main线程执行第" + i + "次");
        }
    }

    // 结果: 新线程和main线程交叉执行
}

3.1.2 实现Runable接口实现
package thread;

import com.sun.imageio.plugins.jpeg.JPEGStreamMetadataFormat;

/**
 * demo02
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class Demo02 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println("新启线程第" + i + "次执行");
        }
    }

    public static void main(String[] args) {
        new Thread(new Demo02()).start();
        for (int i = 0; i < 2000; i++) {
            System.out.println("main线程第" + i + "次执行");
        }
        // 两个线程交替执行
    }
}

3.2 多线程同步下载网络图片

package thread;

import thread.util.WebDownloaderUtil;

/**
 * demo3
 * 多线程下载网络图片
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class Demo3 extends Thread {

    /**
     * url
     */
    private String url;
    /**
     * name
     */
    private String name;

    public Demo3(String url, String name) {
        this.url = url;
        this.name = name;
    }

    @Override
    public void run() {
        WebDownloaderUtil.downloader(url,name);
        System.out.println("下载了" + name + "的文件");
    }

    public static void main(String[] args) {
        Demo3 t1 = new Demo3("https://www.baidu.com/link?url=mdAf6qUc5dCTIwLD6Q3jKwmvMcs6CJn5rpEliOxb1B-Pgu34Am-3JXArS5gvLzD4WQcxNpkjnUs82u4-4QeXFglMhVnXJaMQT2D5f7shBaM5P1YlSy6T7n7gDm09saMO2r18Xx874j8MCpanE7RUI9e5ayrUytDgIvMIHs0kbNfDqLN_OTOcz7R3DkdCNs17dhv5nuFNaxvZcUydmYrNI0CkfCS7gr0J18cG52ybGY7G7id_qrozY6ge0X6zVGahnipw_otkqpNj7UW9Jxu1BCtjQhFTNR5oKUBJSk9-Q0X-GaxUd6gbxSdvHttGeilkyZINSs5pHwgLNjgF_cLmeJZzkRpWz0b90mxDxJKw0A3h7a_cDpwfPNxOBSV2lWFPRbr5ELC2ghRP7-7VBx17xiIPfLWvy8064oorvY1aAtdHprwtQWhvQIjmR3d8q4y1qevgdnNGhO6f7crGeODdiMJJBgw4BicsxqoXLiIW-Eg4iPzZlQ7R1AH_sTzR0BS6pm4Cy-Jy8aqZKjAyQCjW0t7JjqN1caYoOD0N1ymSU62K2mytfhqLmMDWde-AiJ1LAb14qUachbDAYRtTA3Nei_&wd=&eqid=8cae01e9000066230000000361343a02","1.jpg");
        Demo3 t2 = new Demo3("https://image.baidu.com/search/detail?ct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=tupain&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=undefined&hd=undefined&latest=undefined&copyright=undefined&cs=67323756,518322526&os=2809338534,1082215187&simid=3123418117,3816291338&pn=25&rn=1&di=13640&ln=295&fr=&fmq=1630812709686_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=0&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%253A%252F%252Fdingyue.ws.126.net%252FMo%253DX4X4Fqvj4wcNACNAdR6VVDsPFblJ4oyKNgmcj60dR41554622336168.jpg%26refer%3Dhttp%253A%252F%252Fdingyue.ws.126.net%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1633404709%26t%3D155921677fb036dbeaba0e702e439475&rpstart=0&rpnum=0&adpicid=0&nojc=undefined","2.jpg");
        Demo3 t3 = new Demo3("https://image.baidu.com/search/detail?ct=503316480&z=undefined&tn=baiduimagedetail&ipn=d&word=tupain&step_word=&ie=utf-8&in=&cl=2&lm=-1&st=undefined&hd=1&latest=0&copyright=0&cs=2977941019,1842819723&os=2489967780,3193237028&simid=3446759049,228943532&pn=46&rn=1&di=9240&ln=295&fr=&fmq=1630812709686_R&fm=&ic=undefined&s=undefined&se=&sme=&tab=0&width=undefined&height=undefined&face=undefined&is=0,0&istype=0&ist=&jit=&bdtype=0&spn=0&pi=0&gsm=10e&objurl=https%3A%2F%2Fgimg2.baidu.com%2Fimage_search%2Fsrc%3Dhttp%253A%252F%252Fimg2.liuxue360.com%252Fphoto%252F2016%252F05%252F21%252F20160521175959003.jpg%26refer%3Dhttp%253A%252F%252Fimg2.liuxue360.com%26app%3D2002%26size%3Df9999%2C10000%26q%3Da80%26n%3D0%26g%3D0n%26fmt%3Djpeg%3Fsec%3D1633404751%26t%3Dcf38a64223ccaf92b7617a9d5543a7eb&rpstart=0&rpnum=0&adpicid=0&nojc=undefined","3.jpg");

        t1.start();
        t2.start();
        t3.start();
        // 每次执行下载的顺序都不一样
    }
}

3.3 买票

package thread;

public class Demo04 implements Runnable {
    /**
     * 票数
     */
    private Integer tickets = 10;

    @Override
    public void run() {
        while (true) {
            if (tickets <= 0) {
                break;
            }
            try {
                // 模拟延时
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + "拿到了第" + tickets-- + "张票");
        }
    }

    public static void main(String[] args) {
        Demo04 demo04 = new Demo04();
        new Thread(demo04,"tony").start();
        new Thread(demo04,"lucy").start();
        new Thread(demo04,"黄牛党").start();

        // 多个线程竞争使用统一份资源是导致第线程安全问题
    }
}

3.4 龟兔赛跑

package thread;

/**
 * demo05
 * 龟兔赛跑
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class Demo05 implements Runnable{
    /**
     * 赢家
     */
    private static String winner;
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            if(judge(i)){
                break;
            }
           String  currentThreadName = Thread.currentThread().getName();
            System.out.println(currentThreadName+"跑了"+i+"步");

            // 模拟兔子休息
            if (currentThreadName.equals("乌龟") && i % 10 == 0){
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
        
    }

    /**
     * 判断游戏是否结束
     *
     * @param steps 步骤
     * @return boolean
     */
    private boolean judge(int steps){
        if (winner != null){
            return true;
        }
        if (steps == 100){
            winner = Thread.currentThread().getName();
            System.out.println("赢家是:"+winner);
            return true;
        }
        return false;
    }

    public static void main(String[] args) {
        Demo05 demo05 = new Demo05();
        new Thread(demo05,"乌龟").start();
        new Thread(demo05,"兔子").start();
    }
}

4.线程状态

  • 新生
  • 就绪
  • 运行
  • 阻塞
  • 死亡

在这里插入图片描述

5.常用方法

方法说明
setPriority(int newPriority)设置线程的优先级 范围[1,10]
static void sleep(long mills)让当前线程休眠执行的毫秒数
void join()插队
static void yield()线程礼让
void interrupt ()中断线程(不推荐使用)
boolean isAlive()线程存活状态

6.线程停止

推荐让线程自己停下来(run()执行完毕),不建议采用JDK提供的stop()、destory()等方法。建议使用一个标识位外部改变使得线程执行完毕自然停止。

7.线程休眠

sleep(毫秒数)方法使得当前线程休眠进入堵塞状态,持有对象锁且不会释放锁。

8.线程礼让

让当前正在运行的线程重新进入就绪状态等待CPU调度。

package thread;

/**
 * 线程礼让测试
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class TestYield implements 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) {
        TestYield testYield = new TestYield();
        new Thread(testYield,"线程1").start();
        new Thread(testYield,"线程2").start();
    }
}

9.线程插队

join()插队,等待该线程执行完毕其他线程再执行,且其他线程进入堵塞状态。

package thread;

/**
 * join() 测试
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class TestJoin implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 200; i++) {
            System.out.println(Thread.currentThread().getName() + i);
        }
    }

    public static void main(String[] args) {
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin, "new");
        thread.start();
        for (int i = 0; i < 200; i++) {
            if (i == 100) {
                try {
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + i);
        }
    }
}

10.线程状态

  • Thread.State state = new Thread().getState()
    • NEW 新生状态
    • RUNNABLE 运行状态
    • BLOCKED 阻塞状态
    • WAITING 正在等待另外一个线程执行特定动作
    • TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程
    • TERMINATED 已经退出的线程

11.线程优先级([1,10])

Java提供一个线程调度器根据优先级来调度线程池中处于就绪状态的线程。

  • Thread.MAX_PRIORITY 10
  • Thread.MIN_PRIORITY 1
  • Thread.NORM_PRIORITY 5

默认值为5,在调用start()之前设置优先级,注意:优先级只是确定了CPU调度的概率。

setPriority(【优先级】)
getPriority()

12.守护线程

只要还有一个用户线程,守护现象都还存在。

package thread;

/**
 * 守护线程测试
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class DaemonThreadTest {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();
        Thread thread1 = new Thread(god);

        // 设置为守护线程
        thread1.setDaemon(true);
        thread1.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 < 36500; i++) {
            System.out.println("你开心都度过了第" + i + "天");
        }
        System.out.println("byte");
    }
}

13.线程同步

13.1 并发

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

13.2 线程同步

线程同步是一个等待机制,多个需要同时访问对象的线程进入这个对象的等待池中形成队列,等待前一个线程使用完毕后一个线程才能使用,否则继续等待。

13.3 原理

由于同一进程的不同线程共享同一快内存空间,在带来方便的同时,也带来的访问冲突的问题,为了保证数据的正确性,在访问时加入锁机制synchronized。当一个线程获得对象的排他锁,独占资源,其他线程必须等待。

13.4 带来的问题

  • 阻塞其他线程
  • 加锁释放锁导致了频繁的上下文切换和调度延时,导致了性能损耗
  • 如果一个优先级较高的线程等待优先级低的线程释放锁,会引起性能倒置问题

13.5 同步方法

private关键字可将数据私有化起到一定的保护作用。synchronized加锁保证线程安全,包括两种用法:synchronized 方法和synchronized块。

synchronized控制对像的访问,每个对象对应一把锁,每个synchronized方法都必须获取到对象的锁才能执行,否则就会线程阻塞,方法一旦开始执行就会独占这把锁,直到方法返回才释放锁,后面被阻塞的线程才能继续获取锁执行。如果将一个较大的方法加锁会影响效率。

package thread;

public class Demo04 implements Runnable {
    /**
     * 票数
     */
    private Integer tickets = 10;

    private boolean flag = true;

    @Override
    public void run() {
    /*    while (true) {
            if (tickets <= 0) {
                break;
            }
            try {
                // 模拟延时
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + "拿到了第" + tickets-- + "张票");
        }*/

        while (flag){
            try{
                buy();
            }catch (Exception e){
                e.printStackTrace();
            }
        }

    }

    private synchronized void buy() throws InterruptedException {
        if (tickets <= 0){
            flag = false;
            return;
        }
        Thread.sleep(400);
        System.out.println(Thread.currentThread().getName()+"买了第"+ tickets-- + "张票");
    }

    public static void main(String[] args) {
        Demo04 demo04 = new Demo04();
        new Thread(demo04,"tony").start();
        new Thread(demo04,"lucy").start();
        new Thread(demo04,"黄牛党").start();

        // 多个线程竞争使用统一份资源是导致第线程安全问题
    }
}

14.死锁

多个线程各自占有一些共享资源,并且相互等待其他线程占有的资源才能运行,从而导致两个或者多个线程都在等待对方资源释放,都停止的情况。

14.1 产生条件

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

破坏上述四个条件之一即可避免死锁发生。

package thread;

import com.sun.xml.internal.xsom.XSUnionSimpleType;

/**
 * 死锁的演示
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class DeadLockDemo {
    public static void main(String[] args) {
        new Makeup(0,"lucy").start();
        new Makeup(1,"jack").start();
    }
}


/**
 * 口红
 *
 * @author dingwen
 * @date 2021/09/05
 */
class Lipstick{

}


/**
 * 镜子
 *
 * @author dingwen
 * @date 2021/09/05
 */
class Mirror{

}

class Makeup extends Thread{
    /**
     * 口红
     */
    public static Lipstick lipstick = new Lipstick();
    /**
     * 镜子
     */
    public static Mirror mirror = new Mirror();

    /**
     * 名字
     */
    private String name;
    /**
     * 类型
     */
    private int type;


    public Makeup(int type,String name){
        this.type = type;
        this.name = name;
    }
    @Override
    public void run() {
        try{
            makeup();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
        if (type == 0){
            synchronized (lipstick){
                System.out.println(name+"获得lipstick的锁");
                Thread.sleep(1000);
                synchronized (mirror){
                    System.out.println(name+"获得镜子的锁");
                }
            }
        }else{
            synchronized (mirror){
                System.out.println(name+"获的mirror的锁");
                Thread.sleep(1000);
                synchronized (lipstick){
                    System.out.println(name+"获得lipstick的锁");
                }

            }
        }
    }
}

15.Lock

从JDK5.0开始,Java提供了更强大的同步线程机制——通过显式定义同步锁对象来实现同步,同步锁使用Lock对象充当。
Lock接口是控制躲个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock加锁,线程开始访问资源前必须先获得Lock对象。
ReentrantLock(可重入锁)类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁,释放锁。

package thread;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 测试锁
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();
        new Thread(testLock2,"tony").start();
        new Thread(testLock2,"lucy").start();
    }
}

class TestLock2 implements Runnable {
    int tickNum = 100;
    private final ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true) {
            lock.lock();
            try {
                if (tickNum > 0) {
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "拿到了第" + tickNum-- + "张票");
                } else {
                    break;
                }

            } finally {
                lock.unlock();
            }
        }

    }
}

16.synchronizedlock

  • Lock是显式锁,需要手动开启锁关闭锁
  • Synchronized 是隐式锁,出了作用域自动释放
  • Lock只有代码块锁
  • Synchronized 有代码块锁和方法锁
  • 使用Lock锁,JVM将花费更少的时间来调度线程,性能更好,具有更多的扩展性(提供了更多的子类)
  • 使用顺序: Lock > 同步代码块 > 同步方法

17.线程通信

方法名作用
wait()表示线程一直等待,直到其他线程通知,会释放锁
wait(long timeout)指定等待的毫秒数
notify()唤醒一个处于等待状态的线程
notifyAll()唤醒同一个对象上所有调用了wait()处于等待的线程,优先级别高的线程优先调用

均是Object类的方法,都只能在同步方法和同步代码块中使用,否则抛出异。IIIegalMonitorStateException。

17.1 场景分析

生产者消费者问题。

假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者再从仓库取走产品进行消费。如果仓库中没有产品,则生产者生产产品放入仓库,否则停止生产,等待消费。如果仓库中放有产品,则消费者取走产品消费,否则等待产品生产。

这是一个线程同步问题,生产者与消费者共享一个资源,且相互依赖,互为条件。

  • 对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后再通知消费者消费
  • 对于消费者,消费之后通知生产者生产
17.1.1 管程法
package thread;

/**
 * demo06
 * 生产者消费者问题
 * 管程法
 *
 * @author dingwen
 * @date 2021/09/06
 */
public class Demo06 {
    public static void main(String[] args) {
        // 仓库
        Store store = new Store();
        // 生产者
        Producer producer = new Producer(store);
        // 消费者
        Customer customer = new Customer(store);
        new Thread(producer).start();
        new Thread(customer).start();
    }
}


/**
 * 产品
 * 鸡
 *
 * @author dingwen
 * @date 2021/09/06
 */
class Chicken {
    /**
     * 产品id
     */
    private int  id;

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

    public int getId() {
        return id;
    }
}


/**
 * 商店(仓库)
 *
 * @author dingwen
 * @date 2021/09/06
 */
class Store {
    Chicken[] chickenArr = new Chicken[10];
    int count = 0;

    /**
     * 生产者生产产品放入仓库
     *
     * @param chicken 鸡
     */
    public synchronized void push(Chicken chicken) {
        if (count == chickenArr.length) {
            // 仓库已满  等待消费
            // 生产者等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        chickenArr[count] = chicken;
        count++;

        // 已经有产品通知消费者消费
        this.notifyAll();
    }


    /**
     * 消费者消费
     *
     * @return {@link Chicken}
     */
    public synchronized Chicken pop(){
        if(count == 0){
            // 没有产品了 等待生产着生产
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 消费了产品通知生产者生产
        this.notifyAll();
        // 数组下标从零开始
        return chickenArr[--count];
    }

}


/**
 * 生产商
 *
 * @author dingwen
 * @date 2021/09/06
 */
class Producer implements Runnable{
    /**
     * 仓库
     */
    public Store store;

    public Producer(Store store) {
        this.store = store;
    }


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

/**
 * 消费者
 *
 * @author dingwen
 * @date 2021/09/06
 */
class Customer implements Runnable{
    /**
     * 仓库
     */
    public Store store;

    public Customer(Store store) {
        this.store = store;
    }


    /**
     * 消费者消费
     */
    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println("消费者消费了第" + (store.pop().getId()) + "鸡");
        }
    }
}
17.1.2 信号灯法
package thread;

/**
 * demo07
 * 生产者 消费者问题
 * 信号灯法解决
 *
 * @author dingwen
 * @date 2021/09/08
 */
public class Demo07 {
    public static void main(String[] args) {
        Television television = new Television();
        new Thread(new Player(television)).start();
        new Thread(new Watcher(television)).start();
    }
}


/**
 * 电视节目
 *
 * @author dingwen
 * @date 2021/09/08
 */
class Television {
    /**
     * 节目名称
     */
    String name;

    /**
     * 标志位 已有节目可以观看
     */
    boolean flag = true;


    /**
     * 演员表演
     *
     * @param name name
     */
    public synchronized void play(String name) {
        if (!flag) {
            // 已经有节目了演员等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 没有节目,演员表演
        System.out.println("演员表演了" + name + "节目");
        // 通知观众观看
        this.notifyAll();
        this.name = name;
        this.flag = !this.flag;
    }


    public synchronized void watch() {
        if (flag) {
            // 没有节目 观众等待
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        // 已经有节目,观众观看
        System.out.println("观众观看了" + name + "节目");
        // 通知演员表演
        this.notifyAll();
        this.flag = !this.flag;
    }

}


/**
 * 生产者 演员
 *
 * @author dingwen
 * @date 2021/09/08
 */
class Player implements Runnable{

    /**
     * 电视节目
     */
    public Television television;

    public Player(Television television) {
        this.television = television;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            television.play("快乐大本营");
        }
    }
}


class Watcher implements Runnable{

    /**
     * 电视节目
     */
    public Television television;

    public Watcher(Television television) {
        this.television = television;
    }

    @Override
    public void run() {
        for (int i = 10; i > 0; i--) {
            television.watch();
        }
    }
}

18.线程池

  • 背景:经常销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大
  • 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用
  • 好处
    • 提高响应速度(减少了创建新线程的时间)
    • 降低资源消耗(重复利用线程池中线程,不用每次都创建)
    • 便于线程管理
      • corePoolSize (核心池大小)
      • maximumPoolSize (最大线程数)
      • keepAliveTime (当线程没有任务,保持多长时间终止)

19.lambda 理解

外部类、静态内部类、局部内部类、匿名内部类。

package thread;

/**
 * 测试λ
 *
 * @author dingwen
 * @date 2021/09/05
 */
public class TestLambda {

    /**
     * like2
     * 静态内部类实现
     *
     * @author dingwen
     * @date 2021/09/05
     */
    static class Like2 implements ILike{
        @Override
        public void lambda() {
            System.out.println("i like lambda");
        }
    }

    public static void main(String[] args) {
        Like2 like2 = new Like2();
        Like like = new Like();
        like.lambda();
        like2.lambda();

        /**
         * 局部内部类
         *
         * @author dingwen
         * @date 2021/09/05
         */
        class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("i like lambda");
            }
        }

        Like3 like3  = new Like3();
        like3.lambda();

        // 匿名内部类
        ILike like4 = new ILike() {
            @Override
            public void lambda() {
                System.out.println("i like lambda");
            }
        };
        like4.lambda();


    }
}


interface ILike {
    /**
     * λ
     */
    void lambda();
}


/**
 * 外部类
 *
 * @author dingwen
 * @date 2021/09/05
 */
class Like implements ILike{
    @Override
    public void lambda() {
        System.out.println("i like lambda");
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

dingwen_blog

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

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

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

打赏作者

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

抵扣说明:

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

余额充值