【狂神说Java】多线程详解

【狂神说Java】多线程详解

1.任务

生活中的例子、边吃饭、边玩手机

在这里插入图片描述

开车、打电话、挂点滴

在这里插入图片描述

上厕所、玩手机

在这里插入图片描述

现实中太多这样同时做多件事情的例子了,看起来是多个任务都在做,其实本质上我们的大脑在同一时间依旧只做了一件事情。

在这里插入图片描述

2.进程

在操作系统中运行的程序就是进程,比如你的QQ、播放器、游戏、IDE等等。。。

在这里插入图片描述

一个进程可以有多个线程,如视频中同时听声音,看图像,看弹幕,等等

1.说起进程,就不得不说下程序。程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
  2.而进程是执行程序的一次执行过程,它是动态的概念。是系统资源分配的单位
  3.通常在一个进程中可以包含若干个线程,当然一个进程至少有一个线程,不然没有存在的意义。线程是CPU调度和执行的单位

注意:很多 多线程是模拟出来的,真正的多线程是指有多个CPU,即多核,如服务器。如果是模拟出来的 多线程,即在一个CPU的情况下,在同一时间点,cpu 只能执行一个代码,因为切换的很快,所以有同时执行的错觉。

总结:
  1.线程就是独立的执行路径;
  2.在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
  3.main()称之为主线程,为系统的入口,用于执行整个程序;
  4.在一个进程中,如果开辟了多个线程,线程的运行由调度器(CPU)安排调度,调度器是与操作系统紧密相关的,先后顺序是不能人为的干预的。
  5.对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制。
  6.线程会带来额外的开销,如cpu调度时间,并发控制开销。
  7.每个线程在自己的工作内存交互,内存控制不当会造成数据不一致;

3.多线程

原来是一条路,慢慢因为车太多了,道路堵塞,效率极低。
  为了提高使用的频率,能够充分利用道路,于是加了多个车道。

在这里插入图片描述

在这里插入图片描述

原来的方法调用

在这里插入图片描述

多线程下的方法调用

在这里插入图片描述

4.线程创建

线程实现三种方式
  1.继承Thread类
  2.实现Runnable 接口
  3.实现Callable 接口

在这里插入图片描述

继承Thread类实现多线程步骤如下:
  1.自定义线程类继承Thread类
  2.重写run() 方法,编写线程执行体
  3.创建线程对象,调用start() 方法启动线程

创建线程方式1:继承Thread(通过查看源码发现Thread 是实现Runnable接口的)
注意:线程开启不一定立即执行,由CPU调度安排。

//创建线程方式一:继承Thread类,重写run()方法,调用start开启线程
public class TestThread1 extends Thread {
    @Override
    public void run() {
        //run方法 线程体
        for (int i = 0; i < 10; i++) {
            System.out.println("我在撸代码--"+i);
        }
    }

    public static void main(String[] args) {
        //创建线程对象
        TestThread1 testThread1 = new TestThread1();
        //调用start()方法 ,开启线程
        testThread1.start();

        //main线程,主线程
        for (int i = 0; i < 10; i++) {
            System.out.println("我在学习--"+i);
        }

    }
}

//执行结果
我在学习--0
我在学习--1
我在学习--2
我在学习--3
我在学习--4
我在学习--5
我在学习--6
我在学习--7
我在撸代码--0
我在撸代码--1
我在学习--8
我在撸代码--2
我在撸代码--3
我在撸代码--4
我在学习--9
我在撸代码--5
我在撸代码--6
我在撸代码--7
我在撸代码--8
我在撸代码--9

案例:网图下载

在这里插入图片描述

package com.kuang.demo01;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

/**
 * @author by 闲言
 * @classname TestThread2
 * @description 实现多线程同步下图片
 * @date 2021/7/27 18:25
 */
public class TestThread2 extends Thread{

    private String url;//网络图片地址
    private String name;//保存的文件名

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

    /**
     * 下载图片线程的执行体
     */
    @Override
    public void run(){
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downLoader(url,name);
        System.out.println("下载了文件名为:"+name);
    }

    public static void main(String[] args) {
        TestThread2 thread1 = new TestThread2("https://img-blog.csdnimg.cn/img_convert/d8885c9a178b2fcaea732190717b516d.png", "1.jpg");
        TestThread2 thread2 = new TestThread2("https://img-blog.csdnimg.cn/img_convert/d8885c9a178b2fcaea732190717b516d.png", "2.jpg");
        TestThread2 thread3 = new TestThread2("https://img-blog.csdnimg.cn/img_convert/d8885c9a178b2fcaea732190717b516d.png", "3.jpg");
        //先下载t1
        thread1.start();
        //先下载t2
        thread2.start(); 
        //先下载t3
        thread3.start();
    }
}

class WebDownloader{
    public void  downLoader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO 异常,Downloader方法出现问题");
        }
    }

}

//输出
下载了文件名为:2.jpg
下载了文件名为:1.jpg
下载了文件名为:3.jpg

创建线程方式2:实现Runnable接口
在这里插入图片描述

public class TestThread3 implements Runnable{
    @Override
    public void run() {
        //run方法 线程体
        for (int i = 0; i < 10; i++) {
            System.out.println("我在撸代码--"+i);
        }
    }

    public static void main(String[] args) {
        //创建线程对象
        TestThread3 testThread3 = new TestThread3();
        //创建线程对象,通过线程对象来开启我们的线程
        Thread thread = new Thread(testThread3);
        thread.start();

        //main线程,主线程
        for (int i = 0; i < 10; i++) {
            System.out.println("我在学习--"+i);
        }

    }
}

在这里插入图片描述

初识并发问题

多个线程操作同一个资源的情况下出现不同的线程抢到同一张票,线程不安全,数据紊乱

package com.kuang.demo01;



//多个线程操作同一个对象
//买火车票
//多个线程操作同一个资源的情况下出现不同的线程抢到同一张票,线程不安全,数据紊乱
public class TestThread4 implements Runnable{

  int ticketNums=100;//票数

  @Override
  public void run() {
    while (true)
    {
      if(ticketNums<=0)
      {
        break;
      }
      try {
        Thread.sleep(200);//模拟延时
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticketNums--+"张票");
    }
  }
  public static void main(String[] args) {
    TestThread4 testThread4 = new TestThread4();
    new Thread(testThread4,"小明").start();//1,2,3是线程名字
    new Thread(testThread4,"小花").start();
    new Thread(testThread4,"自己吃").start();
  }
}

在这里插入图片描述

发现问题,拿重了

5.案例:龟兔赛跑

1.首先来个赛道距离,然后要离终点越来越近
  2.判断比赛是否结束
  3.打印出胜利者
  4.龟兔赛跑开始
  5.故事中是乌龟赢了,兔子需要睡觉,所以我们模拟兔子睡觉
  6.终于,乌龟赢了

package com.kuang.demo01;



public class Race implements Runnable {

  //胜利者
  private static String winner;

  @Override
  public void run() {
    for (int i = 1; i <= 100; i++) {
      //模拟兔子休息
      if (Thread.currentThread().getName().equals("兔子") || i % 10 == 0) {
        try {
          Thread.sleep(10);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }

      //判断比赛是否结束
      boolean flag = gameOver(i);
      //如果比赛结束了,就停止程序
      if (flag) {
        break;
      }

      System.out.println(Thread.currentThread().getName() + "-->跑了" + i + "步");
    }
  }

  //判断是否完成比赛
  private boolean gameOver(int steps) {
    if (winner != null) {//已经由胜利者了
      return true;
    } else {
      if (steps >= 100) {
        winner = Thread.currentThread().getName();
        System.out.println("Winner is " + winner);
        return true;
      }

    }
    return false;
  }

  public static void main(String[] args) {

    Race race = new Race();
    new Thread(race, "乌龟").start();
    new Thread(race, "兔子").start();

  }

}

在这里插入图片描述

6.静态代理

在这里插入图片描述

静态代理总结:
  1.真实对象和代理对象都要实现同一个接口
  2.代理对象要代理真实角色
好处:
  代理对象可以做很多对象做不了的事情
  真实对象专注做自己的事情

package com.kuang.demo02;


public class StacticProxy {
  public static void main(String[] args) {
    You you = new You();//你要结婚

    WeddingCompany weddingCompany = new WeddingCompany(you);
    weddingCompany.HappyMarry();

    //new Thread( () -> System.out.println("我爱你")).start();

    //new WeddingCompany(new You()).hashCode();
  }
}

interface Marry {
  void HappyMarry();
}

//真实角色,你去结婚
class You implements Marry {

  @Override
  public void HappyMarry() {

    System.out.println("要结婚了,超开心");
  }
}

//真实角色,帮助你结婚
class WeddingCompany implements Marry {
  //代理谁--》真实目标角色
  private Marry target;

  public WeddingCompany(Marry target) {
    this.target = target;
  }

  @Override
  public void HappyMarry() {
    before();
    this.target.HappyMarry();//这就是真实对象
    after();
  }

  private void after() {
    System.out.println("结婚之后,收尾款");
  }

  private void before() {
    System.out.println("结婚之前,布置现场");
  }

}

7.Lambda表达式

在这里插入图片描述

入 希腊字母表中排序第十一位的字母,英语名称为Lambda
避免内部类定义过多
  其实质属于函数式编程概念

(params) -> expression  [表达式]
(params) -> statement [语句]
(params) -> {statement }

new Thread(()-> System.out.println("多线程学习")).start();

为什么要使用lambda 表达式
  1.避免你们内部类定义过多
  2.可以让你的代码看起来很简洁
  3.去掉了一堆没有意义的代码,只留下核心的逻辑

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

public interface Runnable{
    public abstract void run();
}

案例:

package cn.bloghut.lambda;

public class Testlambda {

    //3.静态内部类
    static class Like2 implements ILike {
        @Override
        public void lambda() {
            System.out.println(" i like lambda2");
        }
    }


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

        like = new Like2();
        like.lambda();

        //4.局部内部类
        class Like3 implements ILike {
            @Override
            public void lambda() {
                System.out.println(" i like lambda3");
            }
        }

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

        //6.用lambda表达式
        like = ()->{
            System.out.println(" i like lambda5");
        };
        like.lambda();

    }
}

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

//2.实现类
class Like implements ILike {

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

public class TestLambda2 {
    public static void main(String[] args) {
        // 1、lambda表达式简化
        ILove love = (int a) -> {
            System.out.println("love you-->" + a);
        };
        // 简化1:去掉参数类型
        love = (a)-> {
            System.out.println("love you-->" + a);
        };
        // 简化2:简化括号
        love = a-> {
            System.out.println("love you-->" + a)
            System.out.println("love you-->" + a);
        };
        // 简化3:去掉花括号
        love = a-> System.out.println("love you-->" + a);
        
        love.love(2);
    }
}

interface ILove { 
    void love(int a);
}

class Love implements ILove {
    @Override
    public void love(int a) {
        System.out.println("love you-->" + a);
    }
}

线程状态

在这里插入图片描述

在这里插入图片描述

线程方法

在这里插入图片描述

8.线程停止

在这里插入图片描述

1.建议线程正常停止----》利用次数。不建议死循环
  2.建议使用标志位----》设置一个标志位
  3.不用使用stop或destory 等过时或者JDK 不建议使用的方法


public class TestStop implements Runnable {

    //1.设置一个标志位
    private boolean flag = true;

    @Override
    public void run() {
        int i = 0;
        while (flag) {
            System.out.println("run...Thread->" + 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("该线程停止了");
            }
        }

    }
}



9.线程休眠(sleep)

1.sleep(时间)指定当前线程阻塞的毫秒数;
  2.sleep 存在异常InterruptedException;
  3.sleep 时间达到后线程进入就绪状态
  4.sleep 可以模拟网络延时,倒计时等。
  5.每一个对象都有一个锁,sleep不会释放锁;

在这里插入图片描述

/**
 * 模拟网络延迟:放大问题的发生性
 */
public class TestSleep implements Runnable {

  //票数
  private int ticketNums = 10;

  @Override
  public void run() {
    while (true) {
      if (ticketNums <= 0) {
        break;
      }
      //捕获异常
      try {
        Thread.sleep(200);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + "--->拿到了第" + ticketNums-- + "张票");
    }
  }

  public static void main(String[] args) {
    TestSleep ticket = new TestSleep();
    new Thread(ticket, "小红").start();
    new Thread(ticket, "老师").start();
    new Thread(ticket, "黄牛1").start();
  }
}

/**
 * 模拟倒计时
 */
public class TestSleep2 {

  public static void main(String[] args) {
    try {
      tenDown();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  //模拟倒计时
  public static void tenDown() throws InterruptedException {
    int num = 10;//10秒
    while (true) {
      Thread.sleep(1000);
      System.out.println(num--);
      if (num <= 0) {
        break;
      }
    }
  }
}

/**
 * 每一秒获取当前时间
 */
public class TestSleep3 {

  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 (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}

10.线程礼让

在这里插入图片描述

/**
 * 测试礼让线程
 * 礼让不一定成功,看cpu心情
 */
public class TestYield {
  public static void main(String[] args) {
    MyYeild myYeild = new MyYeild();
    new Thread(myYeild, "a").start();
    new Thread(myYeild, "b").start();
  }
}

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

11.线程插队

在这里插入图片描述

/**
 * 测试join
 * 插队
 */
public class TestJoin implements Runnable {
  @Override
  public void run() {
    for (int i = 0; i < 500; i++) {
      System.out.println("线程vip" + i);
    }
  }

  public static void main(String[] args) throws InterruptedException {
    //启动我们的线程
    TestJoin joinThread = new TestJoin();
    Thread thread = new Thread(joinThread);
    thread.start();

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

12.线程状态观测

在这里插入图片描述

/**
 * 观察测试线程状态
 */
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(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);//Run
    while (state != Thread.State.TERMINATED) {//只要线程不终止,就一直输出状态
      Thread.sleep(100);
      state = thread.getState();//更新线程状态
      System.out.println(state);
    }
    //死亡后的线程不能再启动了,启动会报异常
    //thread.start();
  }
}

13.线程优先级

在这里插入图片描述

/**
 * 线程优先级
 */
public class TestPriority{
  public static void main(String[] args) {
    //主线程默认优先级
    System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());

    MyPriority myPriority = new MyPriority();
    Thread thread1 = new Thread(myPriority);
    Thread thread2 = new Thread(myPriority);
    Thread thread3 = new Thread(myPriority);
    Thread thread4 = new Thread(myPriority);
    Thread thread5 = new Thread(myPriority);

    //先设置优先级,再启动
    thread1.start();

    thread2.setPriority(1);
    thread2.start();

    thread3.setPriority(4);
    thread3.start();

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

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

14.守护(daemon)线程

在这里插入图片描述

15.线程同步

**多个线程操作同一个资源 **

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

16.不安全的线程案例

public class UnsafeTicket {
  public static void main(String[] args) {
    BuyTicket buyTicket = new BuyTicket();
    new Thread(buyTicket, "张三").start();
    new Thread(buyTicket, "李四").start();
    new Thread(buyTicket, "王五").start();
  }
}

class BuyTicket implements Runnable {
  //票
  private int ticketNums = 10;
  boolean flag = true;

  @Override
  public void run() {
    //买票
    while (flag) {
      try {
        buy();
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }

  //买票
  private void buy() {
    //判断是否有票
    if (ticketNums <= 0) {
      flag = false;
      return;
    }
    //延迟
    try {
      Thread.sleep(1);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }

    //买票
    System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
  }
}

/**
 * 不安全的取钱
 */
public class UnsafeBank {
  public static void main(String[] args) {
    Account account = new Account(100, "结婚基金");
    Drawing you = new Drawing(account, 50, "展堂");
    Drawing girlfriend = new Drawing(account, 100, "sad");
    you.start();
    girlfriend.start();
  }
}

//账户
class Account {
  int money;//余额
  String cardName;//卡名

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

//银行:模拟取款
class Drawing extends Thread {
  Account account;//账户
  int drawingMoney;//取金额
  int nowMoney;//你手里的钱

  public Drawing(Account account, int drawingMoney, String name) {
    super(name);
    this.account = account;
    this.drawingMoney = drawingMoney;
  }

  //取钱
  @Override
  public void run() {
    //判断是否有钱
    if (account.money - drawingMoney < 0) {
      System.out.println(Thread.currentThread().getName() + "余额不足,不能进行取钱");
      return;
    }
    try {
      Thread.sleep(1000);//放大问题的发生性
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    //卡内金额 = 余额-你的钱
    account.money = account.money - drawingMoney;
    //你手里的钱
    nowMoney = nowMoney + drawingMoney;
    System.out.println(account.cardName + "余额为:" + account.money);
    //this.getName()==Thread.currentThread().getName()
    System.out.println(this.getName() + "手里的钱:" + nowMoney);
  }
}

//线程不安全的集合
public class UnsafeList {
  public static void main(String[] args) {
    List<String> list = new ArrayList<String>();
    for (int i = 0; i < 1000; i++) {
      new Thread(()->{
        list.add(Thread.currentThread().getName());
      }).start();
    }
    System.out.println(list.size());
  }
}

17.同步方法and同步块

在这里插入图片描述

在这里插入图片描述

同步方法,锁的是this

//安全买票
public class SafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket1 buyTicket = new BuyTicket1();
        new Thread(buyTicket, "张三").start();
        new Thread(buyTicket, "李四").start();
        new Thread(buyTicket, "王五").start();
    }
}

class BuyTicket1 implements Runnable {
    //票
    private int ticketNums = 10;
    boolean flag = true;

    @Override
    public void run() {
        //买票
        while (flag) {
            try {
                buy();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //synchronized 同步方法,锁的是this
    private synchronized void buy() {
        //判断是否有票
        if (ticketNums <= 0) {
            flag = false;
            return;
        }
        //延迟
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        //买票
        System.out.println(Thread.currentThread().getName() + "拿到" + ticketNums--);
    }
}

在这里插入图片描述

锁的对象就是变量的量,需要增删改查的对象

/**
 * 安全的取钱 同步块
 */
public class UnsafeBank {
    public static void main(String[] args) {
        Account1 account = new Account1(100, "结婚基金");
        Drawing1 you = new Drawing1(account, 50, "展堂");
        Drawing1 girlfriend = new Drawing1(account, 100, "sad");
        you.start();
        girlfriend.start();
    }
}

//账户
class Account1 {
    int money;//余额
    String cardName;//卡名

    public Account1(int money, String cardName) {
        this.money = money;
        this.cardName = cardName;
    }
}

//银行:模拟取款
class Drawing1 extends Thread {
    Account1 account;//账户
    int drawingMoney;//取金额
    int nowMoney;//你手里的钱

    public Drawing1(Account1 account, int drawingMoney, String name) {
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    //取钱
    @Override
    public void run() {
        //锁的对象就是变量的量,需要增删改查的对象
        synchronized (account) {
            //判断是否有钱
            if (account.money - drawingMoney < 0) {
                System.out.println(Thread.currentThread().getName() + "余额不足,不能进行取钱");
                return;
            }
            try {
                Thread.sleep(1000);//放大问题的发生性
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //卡内金额 = 余额-你的钱
            account.money = account.money - drawingMoney;
            //你手里的钱
            nowMoney = nowMoney + drawingMoney;
            System.out.println(account.cardName + "余额为:" + account.money);
            //this.getName()==Thread.currentThread().getName()
            System.out.println(this.getName() + "手里的钱:" + nowMoney);
        }
    }

//线程安全的集合 同步块
public class SafeList {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                synchronized (list) {
                    list.add(Thread.currentThread().getName());
                }
            }).start();
        }
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

JUC安全集合类型扩充

//测试JUC安全类型的集合
public class ThreadJuc {
    public static void main(String[] args) {
        CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                list.add(Thread.currentThread().getName());
            }).start();
        }
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

18.死锁

在这里插入图片描述

案例:

/**
 * 死锁:多个线程互相抱着对方需要的资源,然后形成僵持
 * 解决:一个锁只锁一个对象
 */
public  class DeadLock {
  public static void main(String[] args) {
    Makeup makeup = new Makeup(0, "灰姑娘");
    Makeup makeup1 = new Makeup(1, "白雪公主");
    makeup.start();
    makeup1.start();
  }
}

//口红
class Lipstick { }
//镜子
class Mirror { }

class Makeup extends Thread {
  //需要的资源只有一份,用static保证只有一份
  static Lipstick lipstick = new Lipstick();
  static Mirror mirror = new Mirror();
  int choice;//选择
  String girlName;//使用化妆品的人

  public Makeup(int choice, String girlName) {
    this.choice = choice;
    this.girlName = girlName;
  }

  @Override
  public void run() {
    //化妆
    try {
      makeup();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  private void makeup() throws InterruptedException {
    if (choice == 0) {
      synchronized (lipstick) {//获得口红的锁
        System.out.println(this.girlName + "获得口红的锁");
        Thread.sleep(1000);
        synchronized (mirror) {//一秒钟后想获得镜子
          System.out.println(this.girlName + "获得镜子的锁");
        }
      }
    } else {
      synchronized (mirror) {//获得口红镜子
        System.out.println(this.girlName + "获得镜子的锁");
        Thread.sleep(2000);
        synchronized (lipstick) {//二秒钟后想获得的锁
          System.out.println(this.girlName + "获得口红的锁");
        }
      }
    }
  }
}


解决:

/**
 * 死锁:多个线程互相抱着对方需要的资源,然后形成僵持
 * 解决:一个锁只锁一个对象
 */
class DeadLock {
    public static void main(String[] args) {
        Makeup makeup = new Makeup(0, "灰姑娘");
        Makeup makeup1 = new Makeup(1, "白雪公主");
        makeup.start();
        makeup1.start();
    }
}

//口红
class Lipstick { }
//镜子
class Mirror { }

class Makeup extends Thread {
    //需要的资源只有一份,用static保证只有一份
    static Lipstick lipstick = new Lipstick();
    static Mirror mirror = new Mirror();
    int choice;//选择
    String girlName;//使用化妆品的人

    public Makeup(int choice, String girlName) {
        this.choice = choice;
        this.girlName = girlName;
    }

    @Override
    public void run() {
        //化妆
        try {
            makeup();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void makeup() throws InterruptedException {
        if (choice == 0) {
            synchronized (lipstick) {//获得口红的锁
                System.out.println(this.girlName + "获得口红的锁");
                Thread.sleep(1000);
            }
            synchronized (mirror) {//一秒钟后想获得镜子
                System.out.println(this.girlName + "获得镜子的锁");
            }
        } else {
            synchronized (mirror) {//获得口红镜子
                System.out.println(this.girlName + "获得镜子的锁");
                Thread.sleep(2000);
            }
            synchronized (lipstick) {//二秒钟后想获得的锁
                System.out.println(this.girlName + "获得口红的锁");
            }
        }
    }
}

在这里插入图片描述

19.Lock(锁)

在这里插入图片描述

在这里插入图片描述

//测试Lock锁
public class TestLock {
  public static void main(String[] args) {
    TestLock2 testLock = new TestLock2();
    new Thread(testLock).start();
    new Thread(testLock).start();
    new Thread(testLock).start();
  }
}

class TestLock2 implements Runnable {
  int tickerNums = 10;
  //定义Lock锁
  private final ReentrantLock lock = new ReentrantLock();

  @Override
  public void run() {
    while (true) {
      //加锁
      try {
        lock.lock();
        if (tickerNums <= 0) {
          break;
        }
        try {
          Thread.sleep(1000);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(tickerNums--);
      } catch (Exception e) {
        e.printStackTrace();
      } finally {
        //解锁
        lock.unlock();
      }
    }
  }
}

在这里插入图片描述

20.线程通信问题

生产者消费者模式的问题

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


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

//生产者
class Producer extends Thread {
  //容缓冲区
  SynContainer container;

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

  //生产
  @Override
  public void run() {
    for (int i = 0; i < 100; i++) {
      container.push(new Product(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 Product {
  int id;//产品编号

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

//缓冲区
class SynContainer {
  //需要一个容器大小
  Product[] products = new Product[10];
  //容器计数器
  int count = 0;

  //生产者放入产品
  public synchronized void push(Product product) {
    //如果容器满了,需要等待消费者消费
    /*如果是if的话,假如消费者1消费了最后一个,这是index变成0此时释放锁被消费者2拿到而不是生产者拿到,这时消费者的wait是在if里所以它就直接去消费index-1下标越界,如果是while就会再去判断一下index得值是不是变成0了*/
    while (count == products.length) {
      //通知消费者消费,等待生产
      try {
        this.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    //如果没有满,需要丢入产品
    products[count] = product;
    count++;
    //通知消费者消费
    this.notifyAll();
  }

  //消费者消费产品
  public synchronized Product pop() {
    //判断是否能消费
    while (count <= 0) {
      //等待生产者生产
      try {
        this.wait();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
    //如果可以消费
    count--;
    Product product = products[count];
    //吃完了 通知生产者生产
    this.notifyAll();
    return product;
  }
}

package SunThread;
//测试: 生产者消费者模型-->利用缓冲区解决:管程法
//生产者 , 消费者 , 产品 , 缓冲区
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;
    }
}

在这里插入图片描述

来判断一个标志位flag,如果为true,就让他等待、如果为false,就让他去通知另外一个人、把两人衔接起来,就像咱们的信号灯红灯停,绿灯行,通过这样一个判断方式,只要来判断什么时候让他等待,什么时候将他唤醒就ok。

package SunThread;

//测试生产者消费者问题2:信号灯法,通过标志位解决

public class TestPC_01 {
    public static void main(String[] args) {
        TV tv = new TV();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}

//生产者-->演员
class Player extends Thread {
    TV tv;

    public 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("抖音:记录美好生活");
            }
        }
    }
}

//消费者-->观众
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 {
    //演员表演,观众等待 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;
    }
}

在这里插入图片描述

在这里插入图片描述

//测试线程池
public class TestPool {
    public static void main(String[] args) {
        // 1. 创建服务,擦行间线程池
        // newFixedThreadPool(线程池大小)
        ExecutorService service = Executors.newFixedThreadPool(10);
        //执行
        service.execute(new MyThread());
        service.execute(new MyThread());
        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());
    }
}

总结:
在这里插入图片描述

  • 9
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值