多线程(狂神说)

01线程简介

程序:指令和数据的有序集合,静态。

进程(Process):程序的一次执行过程,动态。是系统资源分配的单位。

线程(Thread):一个进程可包含若干线程。线程是cpu调度和执行的单位。

  • main()称之为主线程,为系统的入口,用于执行整个程序

  • 每个线程在自己的工作内存交互,内存控制不当会造成数据的不一致

02线程创建

方法一:

  • 自定义线程类继承Thread类

  • 重写run()方法,编写线程执行体

  • 创建线程对象,调用start()方法启动线程

 //1.继承Thread类
 public class MyThread extends Thread{
 ​
     //2.重写run()方法
     @Override
     public void run() {
         //run方法线程体
         for (int i = 0; i < 20; i++) {
             System.out.println("线程执行体"+i);
         }
     }
     public static void main(String[] args) {
         //main线程,主线程
 ​
         //3.创建线程对象,调用start()方法启动线程
         MyThread myThread =new MyThread();
         myThread.start();
         //testThread1.run();
 ​
         for (int i = 0; i < 20; i++) {
             System.out.println("主线程"+i);
         }
     }
 }

方法二(推荐):

  • 自定义类实现Runnable接口

  • 实现run()方法,编写线程执行体

  • 创建线程对象,调用start()方法启动线程

将实现Runnable接口的对象作为参数传入Thread对象的构造方法中

 //1.实现Runnable接口
 public class MyRunnable implements Runnable{
     //2.实现run()方法
     @Override
     public void run() {
         //run方法线程体
         for (int i = 0; i < 20; i++) {
             System.out.println("线程执行体"+i);
         }
     }
     public static void main(String[] args) {
         //3。创建线程对象,调用start()方法启动线程
         MyThread myThread=new MyThread();
         Thread thread=new Thread(myThread);
         thread.start();
 ​
         for (int i = 0; i < 20; i++) {
             System.out.println("主线程"+i);
         }
     }
 }

方法二便于同一个对象被多个线程使用

run()方法与start()方法区别:

run()方法顺序执行,start()方法并发执行

03并发问题

实现接口方法的快捷键:Alt+Enter try..catch...快捷键:Ctrl+Alt+T

买票

 //多个线程同时操作同一个对象
 //买票的例子
 public class TicketThread implements Runnable{
     //票数
     private int ticketNums = 5;
 ​
     @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) {
         TicketThread ticket=new TicketThread();
 ​
         new Thread(ticket,"小明").start();
         new Thread(ticket,"老师").start();
         new Thread(ticket,"黄牛").start();
     }
 }

可能的结果:

如上图,第2张票同时被小明和黄牛拿到,出现并发问题

龟兔赛跑

 //模拟龟兔赛跑:谁先跑到100步谁赢
 public class Race implements Runnable{
 ​
     private static String winner;
     private int steps;
 ​
     @Override
     public void run() {
         while(true) {
             //兔子睡觉
             if(Thread.currentThread().getName()=="兔子"&&steps%10==0){
                 try {
                     Thread.sleep(10);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
             //flag判断是否有胜利者
             boolean flag=gameOver(steps);
             if(flag) break;
             System.out.println(Thread.currentThread().getName()+"跑了"+(++steps)+"步");
         }
     }
     public boolean gameOver(int steps){
         //如果有winner了,就不会再有winner
         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) {
         //一条赛道
         Race race=new Race();
         
         new Thread(race,"兔子").start();
         new Thread(race,"乌龟").start();
     }
 }
 ​

04Lambda表达式

为什么要使用lambda表达式

  • 避免匿名内部类定义过多

  • 使代码简洁。去掉无意义代码,只留下核心逻辑

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

我们可以使用lambda表达式创建函数式接口的对象

lambda表达式是用于简化函数式接口

 //推导lambda表达式
 public class Lambda {
     //3.静态内部类
     static class Like2 implements ILike {
         @Override
         public void lambda() {
             System.out.println("I like lambda2");
         }
     }
     
     public static void main(String[] args) {
 ​
         //4.局部内部类
         class Like3 implements ILike {
             @Override
             public void lambda() {
                 System.out.println("I like lambda3");
             }
         }
 ​
         ILike like=new Like1();
         like.lambda();
 ​
         like=new Like2();
         like.lambda();
 ​
         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 Like1 implements ILike{
     @Override
     public void lambda() {
         System.out.println("I like lambda1");
     }
 }

Lambda表达式练习:

 public class LambdaTest {
     public static void main(String[] args) {
 //        ILove love=(String name)->{
 //            System.out.println("I love "+name);
 //        };
         //简化1:去掉参数类型   String
 //        ILove love=(name)->{
 //            System.out.println("I love "+name);
 //        };
         //简化2:去掉括号:只有一个参数时才能去掉括号
 //        ILove love=name->{
 //            System.out.println("I love "+name);
 //        };
         //简化3:去掉{}:只有方法体中只有一行语句才可以去掉{}
         ILove love=name-> System.out.println("I love "+name);
 ​
         love.love("Z");
     }
 }
 ​
 interface ILove{
     void love(String name);
 }

05静态代理

  1. 真实对象和代理对象实现同一个接口

  2. 代理对象代理真实角色

  3. 好处:

    1. 代理对象可以做真实对象做不了的事情

    2. 真实对象可以专注做自己的事情

 public class StaticProxy {
     public static void main(String[] args) {
 //        Person person=new Person();//人要结婚
 //        WeddingCompany weddingCompany=new WeddingCompany(person);//婚庆公司代理
 //        weddingCompany.HappyMarray();
 ​
         //线程对比静态代理
         //new Thread(()-> System.out.println("我爱你")).start();
         new WeddingCompany(new Person()).HappyMarray();
         
     }
 }
 ​
 interface Marray{
     void HappyMarray();
 }
 ​
 //真实角色,男方
 class Person implements Marray{
 ​
     @Override
     public void HappyMarray() {
         System.out.println("男方正在结婚");
     }
 }
 ​
 //代理角色,帮助男方去结婚
 class WeddingCompany implements Marray{
 ​
     //代理谁-->真实目标角色
     private Marray target;
 ​
     public WeddingCompany(Marray target){
         this.target=target;
     }
 ​
     @Override
     public void HappyMarray() {
         before();
         this.target.HappyMarray();//真实对象
         after();
     }
 ​
     private void before() {
         System.out.println("结婚前,布置现场");
     }
     private void after() {
         System.out.println("结婚后,收尾款");
     }
 }

06线程状态

五大状态:


 

01线程停止:

 //线程停止
 //1.建议线程正常停止-->利用次数,不建议死循环
 //2.建议使用标志位-->设置一个标志位flag
 public class ThreadStop implements Runnable{
     //设置一个标志位
     private boolean flag=true;
     
     @Override
     public void run() {
         if (flag){
             System.out.println("线程体正在执行");
         }
     }
 ​
     //设置停止线程执行的方法
     public void  stop(){
         this.flag=false;
     }
 ​
     public static void main(String[] args) {
         ThreadStop threadStop=new ThreadStop();
         new Thread(threadStop).start();
 ​
         for (int i = 0; i < 100; i++) {
             if (i==99) threadStop.stop();
             System.out.println("主线程执行--"+i);
         }
     }
 }

02线程休眠_sleep

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

  • sleep存在InterruptedException异常;

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

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

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

sleep模拟倒计时:

 //模拟倒计时
 public class ThreadSleep{
 ​
     //模拟倒计时的方法
     public static void tenDown(int num){
         while(true) {
             try {
                 Thread.sleep(1000);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             System.out.println(num--);
 ​
             if(num<=0) break;
         }
     }
 ​
     public static void main(String[] args) {
         //3秒倒计时
         tenDown(3);
     }
 }

03线程礼让_yield

  • 让当前正在执行的线程暂停,但不阻塞

  • 将线程从运行态转为就绪态

  • 让CPU重新调度,礼让不一定成功

 public class ThreadYield implements Runnable{
     public static void main(String[] args) {
         ThreadYield threadYield=new ThreadYield();
 ​
         new Thread(threadYield,"a").start();
         new Thread(threadYield,"b").start();
     }
 ​
     @Override
     public void run() {
         System.out.println(Thread.currentThread().getName()+"线程开始运行");
         Thread.yield();//线程礼让
         System.out.println(Thread.currentThread().getName()+"线程运行结束");
     }
 }

礼让成功:


礼让不成功:

 

04线程强制执行_join

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

  • “插队”

 public class ThreadJoin implements Runnable{
     @Override
     public void run() {
         for (int i = 0; i < 10; i++) {
             System.out.println(Thread.currentThread().getName()+"线程来插队了"+i);
         }
     }
 ​
     public static void main(String[] args) throws InterruptedException {
 ​
         ThreadJoin threadJoin=new ThreadJoin();
         Thread thread=new Thread(threadJoin,"插队");
         thread.start();
 ​
         //主线程
         for (int i = 0; i < 10; i++) {
             if(i==5){
                 thread.join();
             }
             System.out.println("main正在执行"+i);
         }
     }
 }

运行结果:

 

05线程状态观测

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

    • NEW 尚未启动的线程处于此状态。

    • RUNNABLE 在Java虚拟机中执行的线程处于此状态。

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

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

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

    • TERMINATED 已退出的线程处于此状态。

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

 public class ThreadState {
     public static void main(String[] args) throws InterruptedException {
         Thread thread = new Thread(()->{
             for (int i = 0; i < 5; i++) {
                 try {
                     Thread.sleep(500);
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 }
             }
                 System.out.println("线程正在执行");
         });
 ​
         //观察状态
         //thread.getState()的返回值类型为Thread类中的State(Thread.State)
         Thread.State state = thread.getState();
         System.out.println(state);//NEW
 ​
         //线程启动后
         thread.start();
         System.out.println(thread.getState());//RUNNABLE
 ​
         while(state!=Thread.State.TERMINATED){
             Thread.sleep(200);//主线程休眠200ms
             state=thread.getState();//state更新
             System.out.println(state);
         }
         //thread.start();//出错,因为线程死亡后,不能再次启动(即线程只能启动一次)
     }
 ​
 }

运行结果:

 

07线程优先级

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

    • Thread.MAX_PRIORITY=10

    • Thread.NORM_PRIORITY=5

    • Thread.MIN_PRIORITY=1

  • 优先级越高,越可能被CPU调用

  • 获得优先级的方法:getPriority()

  • 改变优先级的方法:setPriority(int xxx)

     public class ThreadPriority {
     ​
         public static void main(String[] args) {
     ​
             System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
     ​
             Runnable myPrority = new MyPrority();
     ​
             Thread t1 = new Thread(myPrority,"t1");
             Thread t2 = new Thread(myPrority,"t2");
             Thread t3 = new Thread(myPrority,"t3");
             Thread t4 = new Thread(myPrority,"t4");
             Thread t5 = new Thread(myPrority,"t5");
             Thread t6 = new Thread(myPrority,"t6");
     ​
             t1.start();//5
     ​
             t2.setPriority(Thread.MIN_PRIORITY);//1
             t2.start();
     ​
             t3.setPriority(3);
             t3.start();
     ​
             t4.setPriority(Thread.NORM_PRIORITY);//5
             t4.start();
     ​
             t5.setPriority(Thread.MAX_PRIORITY);//10
             t5.start();
     ​
             //t6.setPriority(11);//越界
             //t6.setPriority(0);//越界
             t6.start();
         }
     }
     ​
     class MyPrority implements Runnable{
         @Override
         public void run() {
             System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority());
         }
     }

    08守护线程

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

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

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

  • 守护线程:记录操作日志,监控内存,垃圾回收

 //测试守护线程
 //上帝守护者人们
 public class DaemonThread {
     public static void main(String[] args) {
         God god = new God();
         People people = new People();
 ​
         //设置守护线程
         Thread thread=new Thread(god);
         thread.setDaemon(true);//默认false表示用户线程,true为守护线程
         thread.start();
 ​
         new Thread(new People()).start();
     }
 ​
 }
 ​
 //God线程
 class God implements Runnable{
 ​
     @Override
     public void run() {
         while(true){
         System.out.println("上帝守护人们");}
     }
 }
 ​
 //People线程
 class People implements Runnable{
 ​
     @Override
     public void run() {
         for (int i = 1; i <= 120; i++) {
             System.out.println("第"+i+"年,人们开心的活着");
         }
         System.out.println("=====Goodbye World!=====");
     }
 }

08线程同步

多个线程操作同一个资源

队列+锁保证线程同步

锁机制 synchronized

01线程不安全案例

不安全的买票:

 public class UnsafeBuyTicket {
     public static void main(String[] args) {
         BuyTicket buyTicket = new BuyTicket();
 ​
         new Thread(buyTicket,"黄牛A").start();
         new Thread(buyTicket,"黄牛B").start();
         new Thread(buyTicket,"黄牛C").start();
         new Thread(buyTicket,"观众").start();
     }
 }
 ​
 class BuyTicket implements Runnable{
 ​
     //票数
     static int ticketNums=10;
     //标志位,用于停止线程
      boolean flag=true;
 ​
     @Override
     public void run() {
         while(flag){
             try {
                 Thread.sleep(300);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
             buy();
         }
     }
 ​
     public  void buy(){
         if (ticketNums<=0){
             flag=false;
             return;
         }
 ​
         System.out.println(Thread.currentThread().getName()+"买到了第"+(ticketNums--)+"张票");
     }
 }

不安全的取钱:

 //模拟,两个人取同一账户里的钱
 public class UnsafeBank {
     public static void main(String[] args) {
         //一个账户
         Account account=new Account(100,"结婚基金");
         //两个人取同一个账户里的钱
         Withdraw man=new Withdraw(account,100,"男方");
         Withdraw woman=new Withdraw(account,50,"女方");
 ​
         man.start();
         woman.start();
     }
 }
 ​
 //账户
 class Account{
     int money;//余额
     String name;//账户名
 ​
     public Account(int money, String name) {
         this.money = money;
         this.name = name;
     }
 }
 ​
 //银行:模拟取钱操作
 class Withdraw extends Thread{
     Account account;//账户
     int withdrawMoney;//要取的钱
     int nowMoney;//取到人手里的钱
 ​
     public Withdraw(Account account,int withdrawMoney,String name){
         //name:线程名
         super(name);
         this.account=account;
         this.withdrawMoney=withdrawMoney;
     }
 ​
     //取钱
     @Override
     public void run() {
         if(account.money-withdrawMoney<0) {
             System.out.println("账户余额不足,取钱失败");
             return;
         }
 ​
         //用sleep放大多线程可能出现的问题
         try {
             Thread.sleep(500);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
 ​
         account.money-=withdrawMoney;
         nowMoney+=withdrawMoney;
 ​
         //这里的this就是Thread.currentThread()
         System.out.println(this.getName()+"取完"+account.name+"账户里的钱后,现在手里的钱为"+nowMoney);
         System.out.println(account.name+"的账户余额为"+account.money);
     }
 }

不安全的集合:

 import java.util.ArrayList;
 import java.util.List;
 ​
 //(这个集合的例子举的不好)
 public class UnsafeList {
     public static void main(String[] args) throws InterruptedException {
 ​
         List<String> list=new ArrayList<String>();
 ​
         for (int i = 0; i < 100; i++) {
             new Thread(()->{                 list.add(Thread.currentThread().getName());
             }).start();
         }
         
         
         Thread.sleep(200);
         sleep是为了让主线程的System.out.println(list.size())在其他线程都运行完再输出(这个集合的例子举的不好)
         System.out.println(list.size());
         //打印出来不一定是100,可能小于00,因为多个线程并发时,可能同时执行list.add(),将两个线程添加到同一内存空间
     }
 }
 ​

02同步方法

  1. 每个对象对应一把锁

  2. 线程若想调用synchronized方法/块,必须获得synchronized所修饰对象的锁,否则线程阻塞;synchronized方法/块一旦执行就独占该锁,直到执行完才释放锁,后面被阻塞的线程才能获得这个锁,继续执行

synchronized的三种应用方式:

synchronized关键字最主要有以下3种应用方式,下面分别介绍

  • 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁

  • 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁

  • 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

我们以方法举例可以有如下的几种情况:

  • 修饰实例方法, 这里默认的锁就是当前类的实例对象

 public synchronized void t0(){
     // 方法体
 }
  • 修饰静态方法 , 这里默认的锁就是当前类的class对象

 public synchronized static void t0(){
     // 方法体
 }
  • 修饰代码块

    • 锁一个对象

     public void t2() {
           synchronized (o) {
             // 方法体
         }
     }
    • 锁一个对象的class

     public void t3() {
           synchronized (o.getClass()) {
             // 方法体
         }
     }

总结:

有两种锁,对象锁和Class锁。

Class锁允许所有的Class相同的对象都拿到资源,

对象锁就单纯的只有这个对象才能获取到锁。

静态都是锁类的 class。其他的时候我们都是习惯于单纯的锁对象。

synchronized用法
  1. synchronized方法:用synchronized修饰方法

eg:public synchronized void method(){}

  1. synchronized块:synchronized(Obj){}

Obj通常是共享资源

不安全的买票:锁 buyTicket

 public synchronized void buy(){
     if (ticketNums<=0){
         flag=false;
         return;
     }

不安全的银行:锁 account

 synchronized (account){
     if(account.money-withdrawMoney<0) {
         System.out.println("账户余额不足,取钱失败");
         return;
     }
 ​
     //用sleep放大多线程可能出现的问题
     try {
         Thread.sleep(500);
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
 ​
     account.money-=withdrawMoney;
     nowMoney+=withdrawMoney;
 ​
     //这里的this就是Thread.currentThread()
     System.out.println(this.getName()+"取完"+account.name+"账户里的钱后,现在手里的钱为"+nowMoney);
     System.out.println(account.name+"的账户余额为"+account.money);
 }

不安全的集合:锁 list

 synchronized (list){
     list.add(Thread.currentThread().getName());
 }

09死锁

死锁产生的四个必要条件(缺一不可)

  1. 互斥条件

  2. 请求与保持条件

  3. 不剥夺条件

  4. 循环等待条件

以下程序会死锁:

 //死锁:多个线程互相持有对方需要的资源不释放,然后形成僵持
 public class DeadLock {
     public static void main(String[] args) {
         MakeUp girl1=new MakeUp(0,"小光");
         MakeUp girl2=new MakeUp(1,"小雪");
 ​
         girl1.start();
         girl2.start();
     }
 }
 ​
 //口红
 class Lipstick{
 }
 ​
 //镜子
 class Mirror{
 }
 ​
 //化妆
 class MakeUp extends Thread{
     //需要的资源只有一份,用static来保证只有一份
     static Lipstick lipstick=new Lipstick();
     static Mirror mirror=new Mirror();
 ​
     int choice;//选择 规定0:先拿口红再拿镜子 1;先拿镜子再拿口红
     String girlName;//使用化妆品的人
 ​
      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 if(choice==1){
              synchronized (mirror){
                  System.out.println(this.girlName+"获得镜子的锁");
                  Thread.sleep(1000);
                  synchronized (lipstick){
                      System.out.println(this.girlName+"获得口红的锁");
                  }
              }
          }
     }
 }

上例:在synchronized块中还有synchronized块

解决方法:将synchronized块中的synchronized块拿出来

 if(choice==0){
     synchronized (lipstick){
         System.out.println(this.girlName+"获得口红的锁");
         Thread.sleep(1000);
     }
     
     synchronized (mirror){
         System.out.println(this.girlName+"获得镜子的锁");
     }
 }else if(choice==1){
     synchronized (mirror){
         System.out.println(this.girlName+"获得镜子的锁");
         Thread.sleep(1000);
     }
     synchronized (lipstick){
         System.out.println(this.girlName+"获得口红的锁");
     }
 }

10Lock锁

  • lock是显示锁(手动开关锁,别忘记关闭锁) synchronized是隐式锁,出了作用域自动释放

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

  • 使用Lock锁,JVM将花费较少的时间来调度线程

 import java.util.concurrent.locks.ReentrantLock;
 ​
 public class TestLock {
     public static void main(String[] args) {
         TestLock2 testLock2=new TestLock2();
 ​
         new Thread(testLock2).start();
         new Thread(testLock2).start();
         new Thread(testLock2).start();
     }
 }
 class TestLock2 implements Runnable{
 ​
     int ticketNums=10;
 ​
     //定义lock锁
     private final ReentrantLock lock=new ReentrantLock();
 ​
     @Override
     public void run() {
         while(true){
 ​
             try{
                 lock.lock();//加锁
                 if (ticketNums>0){
                     try {
                         Thread.sleep(1000);
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                     System.out.println(ticketNums--);
                 }
                 else{
                     break;
                 }
                  }
             finally {
                 lock.unlock();//解锁
             }
         }
     }
 }

11线程协作

生产者消费者问题

wait()与notify()

wait()方法:是让当前线程等待的,即让线程释放了对共享对象的锁,不再继续向下执行。

wait(long timeout)方法:可以指定一个超时时间,过了这个时间如果没有被notify()唤醒,则函数还是会返回。如果传递一个负数timeout会抛出IllegalArgumentException异常。

notify()方法:会让调用了wait()系列方法的一个线程释放锁,并通知其它正在等待(调用了wait()方法)的线程得到锁。

notifyAll()方法会唤醒所有在共享变量上由于调用wait系列方法而被挂起的线程。

注意:

调用wait()、notify()方法时,当前线程必须要成功获得锁(必须写在同步代码块锁中),否则将抛出异常。

 

01管程法

 //测试生产者消费者问题
 //1.管程法:利用缓冲区解决
 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 = 1; i <= 10; 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 = 1; i <= 10; 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;
 ​
         //通知消费者消费
         this.notify();
     }
 ​
     //消费者消费产品
     public synchronized Chicken pop(){
         //判断能否消费
         //若不能消费
         if(count==0){
             //等待生产者生产,消费者等待
             try {
                 this.wait();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
         //若可以消费
         count--;
         Chicken chicken=chickens[count];
 ​
         this.notify();
         return chicken;
     }
 ​
 ​
 }

02信号灯法

 //测试生产者消费者问题
 //2信号灯法,利用标志位解决
 public class TestPC2 {
     public static void main(String[] args) {
         Dishes dish=new Dishes();
 ​
         new Cooker(dish).start();
         new Customer(dish).start();
 ​
     }
 }
 //生产者-->厨师
 class Cooker extends Thread{
     Dishes dish;
     public Cooker(Dishes dish){
         this.dish=dish;
     }
 ​
     @Override
     public void run() {
         for (int i = 0; i < 3; i++) {
             this.dish.cook("好吃的菜"+(i+1));
         }
     }
 }
 ​
 //消费者-->顾客
 class Customer extends Thread{
     Dishes dish;
     public Customer(Dishes dish){
         this.dish=dish;
     }
 ​
     @Override
     public void run() {
         for (int i = 0; i < 3; i++) {
             this.dish.eat();
         }
     }
 }
 ​
 //产品-->菜品
 class Dishes{
     //厨师做菜,顾客等待 T
     //顾客吃菜,厨师等待 F
 ​
     String dish;//表演的节目
     boolean flag=true;
 ​
     //做菜
     public synchronized void cook(String dish){
         if(!flag){
             try {
                 this.wait();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
 ​
         System.out.println("厨师做了:"+dish);
         //通知顾客吃菜
         this.notifyAll();
         this.dish=dish;
         this.flag=!this.flag;
     }
 ​
     //吃菜
     public synchronized void eat(){
         if(flag){
             try {
                 this.wait();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
         System.out.println("顾客吃了:"+dish);
         //通知厨师做菜
         this.notifyAll();
         this.flag=!this.flag;
     }
 }

12线程池

背景:线程经常创建和销毁,会使用特别大的资源

思路:提前创建多个线程,放入线程池中,使用时直接获取,使用完放回池 中。可以避免频繁创建销毁线程,实现重复利用。

好处:

  • 提高响应速度(减少了创建新线程的时间)

  • 降低资源消耗(重复利用线程池中的线程,不需要每次都创建)

  • 便于线程管理

    • corePoolSize:核心池的大小

    • maximumPoolSize:最大线程数

    • keepAliveTime:线程没有任务时最多保持多长时间后会终止

       

      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;

      //测试线程池
      public class TestPool {
          public static void main(String[] args) {
              //1.创建服务,创建线程池
              //newFixedThreadPool()中参数为线程池大小
              ExecutorService service= Executors.newFixedThreadPool(4);

              //2.执行run()
              service.execute(new MyThread2());
              service.execute(new MyThread2());
              service.execute(new MyThread2());
              service.execute(new MyThread2());

              //3.关闭连接
              service.shutdown();
          }
      }

      class MyThread2 implements Runnable{

          @Override
          public void run() {
              for (int i = 0; i < 3; i++) {
                  System.out.println(Thread.currentThread().getName());
              }
          }
      }

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值