线程实现
  • 三种创建线程的方式:

  1. 继承Thread类

  2. 实现Runnable接口

  3. 实现Callable接口(了解)

 

创建线程方式1:继承Thread类

  1. 自定义继承Thread类的线程类

  2. 重写run()方法,线程体

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

  • 注意:两个线程同时执行(异步),这里体现为交替输出

 1  //创建线程:继承Thread类,重写run(),调用start() 2  public class Demo01 extends Thread{ 3      @Override 4      public void run() { 5          //run方法线程体 6          for (int i = 0; i < 2000; i++) { 7              System.out.println("aaaaaaaa"); 8          } 9      }10  11      public static void main(String[] args) {12          //新建线程对象13          Demo01 demo01 = new Demo01();14          //调用start()方法,开启线程15          demo01.start();16          for (int i = 0; i < 2000; i++) {17              System.out.println("bbbbbbbbb");18          }19      }20  }

 

 

Thread下载网络图片

通过Thread多线程下载网络图片

准备工作:下载commonsio包,导入com.lib,添加到库

 1  //Thread练习,实现多线程同步下载网络图片 2  public class Demo02 extends Thread{ 3      private String url; //图片地址 4      private String name;//保存路径 5      //构造器 6      public Demo02(String url,String name){ 7          this.url = url; 8          this.name = name; 9      }10      //下载线程执行体11      @Override12      public void run() {13          WebDownloader webDownloader = new WebDownloader();14          webDownloader.downloader(url,name);15          System.out.println(name+" success");16      }17  18      public static void main(String[] args) {19          //unable to find valid certification path to requested target20          Demo02 t1 = new Demo02("https://","java1.jpg");21          Demo02 t2 = new Demo02("https://","java2.jpg");22          Demo02 t3 = new Demo02("https://","java3.jpg");23          t1.start();24          t2.start();25          t3.start();26      }27  }28  //下载器29  class WebDownloader{30      //下载方法31      public void downloader(String url,String name){32          try {33              //通过FileUtils.copyURLToFile把url转换为文件34              FileUtils.copyURLToFile(new URL(url),new File(name));35          } catch (IOException e) {36              e.printStackTrace();37          }38      }39  }

 

 

创建线程方式2:实现Runnable接口

  1. 定义MyRunnable类实现Runnable接口

  2. 实现run()方法,编写线程体

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

  • 推荐使用,避免了单继承局限性,方便同一个对象被多个线程使用(多个代理)

 1  //创建线程方式2:实现runnable接口,重写run方法,执行线程需要丢入runnable接口实现类,调用start方法 2  public class Demo03 implements Runnable{ 3      @Override 4      public void run() { 5          for (int i = 0; i < 20; i++) { 6              System.out.println("ccc"); 7          } 8      } 9  10      public static void main(String[] args) {11          //创建runnable接口的实现类对象12          Demo03 demo03 = new Demo03();13          //创建线程对象,通过线程对象来开启线程(代理)14  //        Thread thread = new Thread(demo03);15  //        thread.start();16          //以上两句可以简写成以下形式:17          new Thread(demo03).start();18      }19  }

 

  • 例子:龟兔赛跑(使用同一条赛道)

 1  //模拟龟兔赛跑 2  public class Demo05 implements Runnable{ 3   4      private static String winner; 5   6      @Override 7      public void run() { 8          for (int i = 0; i <= 100; i++) { 9              //模拟rabbit休息10              if (Thread.currentThread().getName().equals("rabbit") && i%10==0){11                  try {12                      Thread.sleep(1);13                  } catch (InterruptedException e) {14                      e.printStackTrace();15                  }16              }17              //判断比赛是否结束18              boolean flag = gameOver(i);19              if (flag){20                  break;21              }22  23              System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");24          }25      }26      //判断是否完成比赛27      private boolean gameOver(int steps){28          if (winner != null){29              return true;30          } else {31              if (steps >= 100){32                  winner = Thread.currentThread().getName();33                  System.out.println(winner+" is winner");34                  return true;35              }36          }37          return false;38      }39  40      public static void main(String[] args) {41          //启动线程42          Demo05 demo05 = new Demo05();   //创建赛道43          new Thread(demo05,"turtle").start();44          new Thread(demo05,"rabbit").start();45      }46  }

 


 

 

创建线程方式3:实现Callable接口

  1. 实现Callable接口需要返回值类型

  2. 重写call方法,抛出异常

  3. 创建目标对象

  4. 创建执行服务

  5. 提交执行

  6. 获取结果

  7. 关闭服务

  • 例子:Callable改造下载网络图片

 1  //线程创建方式3:实现callable接口 2  //callable的好处:1、可以定义返回值 2、可以抛出异常 3  //这里下载会出现异常,目前看来是证书的原因,尚未解决 4  public class TestCallable implements Callable<Boolean> { 5   6      private String url; //图片地址 7      private String name;//保存路径 8      //构造器 9      public TestCallable(String url,String name){10          this.url = url;11          this.name = name;12      }13      //下载线程执行体14      @Override15      public Boolean call() {16          WebDownloader webDownloader = new WebDownloader();17          webDownloader.downloader(url,name);18          System.out.println(name+" success");19          return true;20      }21  22      public static void main(String[] args) throws Exception{23          //unable to find valid certification path to requested target24          TestCallable t1 = new TestCallable("https://pic.cnblogs.com/avatar/2281056/20210224183025.png","java1.jpg");25          TestCallable t2 = new TestCallable("https://pic.cnblogs.com/avatar/2281056/20210224183025.png","java2.jpg");26          TestCallable t3 = new TestCallable("https://pic.cnblogs.com/avatar/2281056/20210224183025.png","java3.jpg");27  28          //创建执行服务29          ExecutorService ser = Executors.newFixedThreadPool(3);30          //提交执行(执行call方法)31          Future<Boolean> r1 = ser.submit(t1);32          Future<Boolean> r2 = ser.submit(t2);33          Future<Boolean> r3 = ser.submit(t3);34          //获取结果35          boolean rs1 = r1.get();36          boolean rs2 = r2.get();37          boolean rs3 = r3.get();38          //关闭服务39          ser.shutdownNow();40      }41  }42  43  //下载器44  class WebDownloader{45      //下载方法46      public void downloader(String url,String name){47          try {48              //通过FileUtils.copyURLToFile把url转换为文件49              FileUtils.copyURLToFile(new URL(url),new File(name));50          } catch (IOException e) {51              e.printStackTrace();52          }53      }54  }

 

 

静态代理模式

  • Thread类代理了Runnable接口

  • 静态代理:线程底部的实现原理

 1  //静态代理模式:1、真实对象和代理对象都要实现同一个接口 2、代理对象要代理真实角色 2  //代理的好处:代理对象可以完成很多真实对象做不了的事情,真实对象只需要专注自己的任务 3  public class StacticProxy { 4   5      public static void main(String[] args) { 6          You you = new You();//真实角色 7          WeddingCompany weddingCompany = new WeddingCompany(you); 8          weddingCompany.HappyMarry(); 9          //也可以通过以下方式实现10          //new WeddingCompany(new You()).HappyMarry();11      }12  13  }14  15  //定义接口16  interface Marry{17      void HappyMarry();18  }19  //继承接口并重写方法20  //结婚的人,真实角色21  class You implements Marry{22      @Override23      public void HappyMarry() {24          System.out.println("name");25      }26  }27  //婚庆公司,代理角色28  class WeddingCompany implements Marry{29      //代理目标,真实角色30      private Marry target;31  32      public WeddingCompany(Marry target) {33          this.target = target;34      }35  36      @Override37      public void HappyMarry() {38          before();39          this.target.HappyMarry();40          after();41      }42  43      private void after() {44          System.out.println("cleanup");45      }46  47      private void before() {48          System.out.println("setup");49      }50  }

 

 

Lambda表达式

  • 使用lambda表达式的优点

  1. 避免匿名内部类定义过多

  2. 让代码更简洁,只保留核心的逻辑

函数式接口

只包含唯一一个抽象方法的接口就是一个函数式接口,可以通过lambda表达式创建接口的对象

只有函数式接口才可以使用lambda表达式

推导lambda表达式

 1  //推导lambda表达式 2  //new Thread( (参数)->System.out.println("xxxx") ).start(); 3  public class TestLambda01 { 4   5      //3.静态内部类 6      static class Like2 implements ILike{ 7          @Override 8          public void lambda() { 9              System.out.println("Lambda2");10          }11      }12  13      public static void main(String[] args) {14          ILike like = new Like();15          like.lambda();16          like = new Like2();17          like.lambda();18  19          //4.局部内部类20          class Like3 implements ILike{21              @Override22              public void lambda() {23                  System.out.println("Lambda3");24              }25          }26          like = new Like3();27          like.lambda();28  29          //5.匿名内部类(没有类的名称,必须借助接口或父类)30          like = new ILike() {31              @Override32              public void lambda() {33                  System.out.println("Lambda4");34              }35          };36          like.lambda();37  38          //6.lambda简化(最终形式!)39          like = (/*参数*/) -> {40              System.out.println("Lambda5");41          };42          like.lambda();43      }44  45  }46  47  //1.定义一个函数式接口48  interface ILike{49      void lambda();50  }51  //2.实现类52  class Like implements ILike{53      @Override54      public void lambda() {55          System.out.println("Lambda");56      }57  }

 

  • 简化lambda表达式

 1  //简化lambda表达式 2  public class TestLambda02 { 3      public static void main(String[] args) { 4          //原始lambda表达式 5          Test t1 = null; 6          t1 = (int a)->{ 7              System.out.println("this is " + a); 8          }; 9          t1.speak(10);10          //简化1:简化参数类型(多个参数多行语句时推荐使用!)11          Test t2 = null;        
12          t2 = (a)->{13              System.out.println("this is " + a);14          };15          t2.speak(20);16          //简化2:简化括号(只有一个参数时才能简化为以下形式)17          Test t3 = null;        
18          t3 = a ->{19              System.out.println("this is " + a);20          };21          t3.speak(30);22          //简化3:简化{}(代码只有一行时才能简化为以下形式)23          Test t4 = null;        
24          t4 = a -> System.out.println("this is " + a);25          t4.speak(40);26      }27  }28  //函数式接口29  interface Test{30      void speak(int a);31  }

 

  • lambda表达式在多线程中的应用

 1  //Runnable接口的源码如下 2  //可以看出Runnable是函数式接口,只有一个run方法,通过lambda表达式可以方便地实现Runnable接口 3  public interface Runnable { 4      /** 5       * When an object implementing interface <code>Runnable</code> is used 6       * to create a thread, starting the thread causes the object's 7       * <code>run</code> method to be called in that separately executing 8       * thread. 9       * <p>10       * The general contract of the method <code>run</code> is that it may11       * take any action whatsoever.12       *13       * @see     java.lang.Thread#run()14       */15      public abstract void run();16  }

 

 

线程状态

  • 线程方法

    方法说明
    setPriority(int newPriority)改变线程优先级
    static void sleep(long millis)在指定的毫秒数内让当前正在执行的线程休眠
    void join()等待线程终止
    static void yield()暂停当前正在执行的线程对象,并执行其他线程
    void interrup()中断线程
    boolean isAlive()测试线程是否处于活动状态

线程停止

  • 尽量使线程自己停止

 1 //测试线程停止stop 2 //1.建议使线程正常停止--->利用次数 3 //2.建议使用标志位--->设置标志位,条件满足则终止 4 //3.不要使用stop或destroy等过时方法 5 public class TestStop implements Runnable{ 6  7     //1.设置标志位 8     private boolean flag = true; 9 10     @Override11     public void run() {12         int i = 0;13         while (flag){14             System.out.println("run...Thread" + i++);15         }16     }17 18     //2.设置公开的方法转换标志位,停止线程19     public void stop(){20         this.flag = false;21     }22 23     public static void main(String[] args) {24         TestStop testStop = new TestStop();25 26         new Thread(testStop).start();27 28         for (int i = 0; i < 1000; i++) {29             System.out.println("main" + i);30             if (i == 900){31                 //3.调用stop方法切换标志位,使线程停止32                 testStop.stop();33                 System.out.println("Thread stop");34             }35         }36     }37 38 }

 

线程休眠

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

  • sleep存在异常InterruptedException

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

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

 
 1 //模拟网络延时:放大问题的发生性 2  //线程不安全:多个线程同时操作了同一个对象(多个人抢到同一张票) 3  public class TestSleep implements Runnable{ 4      private int ticketNums = 10; 5      @Override 6      public void run() { 7          while (true){ 8              if (ticketNums <= 0) break; 9              //线程sleep方法模拟延时10              try {11                  Thread.sleep(200);12              } catch (InterruptedException e) {13                  e.printStackTrace();14              }15              //Thread.currentThread().getName() 获取当前线程的名字16              System.out.println(Thread.currentThread().getName()+" got "+ticketNums--);17          }18      }19  20      public static void main(String[] args) {21          Demo04 demo04 = new Demo04();22  23          new Thread(demo04,"user1").start();24          new Thread(demo04,"user2").start();25          new Thread(demo04,"user3").start();26      }27  }28  =============================================================================29  //模拟倒计时30  public class TestSleep2{31  32      public static void countDown() throws InterruptedException{33          int num = 10;34          while (true){35              Thread.sleep(1000);36              System.out.println(num--);37              if (num<0){38                  break;39              }40          }41      }42  43      public static void main(String[] args) throws InterruptedException{44          //打印倒计时45          //countDown();46  47          //复习:打印当前系统时间48          Date startTime = new Date(System.currentTimeMillis());  //获取系统当前时间49          while (true){50              try {51                  Thread.sleep(1000);52                  System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); //SDF时间格式化打印53                  startTime = new Date(System.currentTimeMillis());  //更新当前时间54              } catch (InterruptedException e) {55                  e.printStackTrace();56              }57          }58      }59  60  }

 

线程礼让

  • 礼让线程会使让钱正在执行的线程暂停,但不阻塞(从运行态转为就绪态),CPU重新调度

  • 礼让有可能失败,因为只是将线程重新调度,可能会继续执行原本礼让的线程

 1  //礼让线程 2  public class TestYield { 3      public static void main(String[] args) { 4          MyYield myYield = new MyYield(); 5          new Thread(myYield,"t1").start(); 6          new Thread(myYield,"t2").start(); 7      } 8  } 9  10  class MyYield implements Runnable{11      @Override12      public void run() {13          System.out.println("Thread " + Thread.currentThread().getName() +"start");14          Thread.yield(); //礼让15          System.out.println("Thread " + Thread.currentThread().getName() +"end");16      }17  }

 

线程合并

  • join合并线程,阻塞当前执行的其他线程

 1 //线程合并 2 public class TestJoin implements Runnable{ 3     @Override 4     public void run() { 5         for (int i = 0; i < 100; i++) { 6             System.out.println("somebody cut in line " + i + " times"); 7         } 8     } 9 10     public static void main(String[] args) throws InterruptedException {11         TestJoin testJoin = new TestJoin();12         Thread thread = new Thread(testJoin);13         thread.start();14 15         for (int i = 0; i < 200; i++) {16             if (i == 100){17                 thread.join();//插队,阻塞主线程18             }19             System.out.println("custom "+ i);20         }21     }22 23 }

 

查看线程状态

 1 public class TestStates { 2  3     public static void main(String[] args) throws InterruptedException { 4         Thread thread = new Thread(()->{ 5             for (int i = 0; i < 5; i++) { 6                 try { 7                     Thread.sleep(1000); 8                 } catch (Exception e) { 9                     e.printStackTrace();10                 }11             }12             System.out.println("Zzzzzzz");13         });14 15         Thread.State state = thread.getState();16         System.out.println(state);  //还没调用start方法,线程处于创建态NEW17 18         thread.start();    //调用start后,进程进入就绪态,准备运行19         state = thread.getState();20         System.out.println(state);  //此时线程为运行态RUNNABLE21 22         while (state != Thread.State.TERMINATED){   //只要线程不终止TERMINATED,就一直输出状态23             state = thread.getState();24             System.out.println(state);  //5s内线程处于就绪等待状态TIMED_WAITING25         }26 27         thread.start(); //报错,已经结束的线程不能再次启动28     }29 30 }

 

 

线程优先级

  • Java通过线程调度器监控所有就绪态线程,按优先级决定执行对象

  • 优先级范围为1~10,优先级越高先运行的机率越高,但最终实际运行的线程还是由CPU决定

 1  public class TestPriority { 2      public static void main(String[] args) { 3          //主线程优先级默认5无法更改 4          //getPriority()获取优先级 5          System.out.println(Thread.currentThread().getName()+"---"+Thread.currentThread().getPriority()); 6   7          MyPriority myPriority = new MyPriority(); 8          Thread t1 = new Thread(myPriority); 9          Thread t2 = new Thread(myPriority);10          Thread t3 = new Thread(myPriority);11          Thread t4 = new Thread(myPriority);12          Thread t5 = new Thread(myPriority);13          Thread t6 = new Thread(myPriority);14  15          //在start前先设置优先级,再启动16          //setPriority()设置优先级17          t1.start();18          t2.setPriority(1);19          t2.start();20          t3.setPriority(3);21          t3.start();22          t4.setPriority(Thread.MAX_PRIORITY);//最大优先级1023          t4.start();24          t5.setPriority(Thread.MIN_PRIORITY);//最小优先级125          t5.start();26          t6.setPriority(Thread.NORM_PRIORITY);//中等优先级527          t6.start();28      }29  }30  31  class MyPriority implements Runnable{32      @Override33      public void run() {34          //打印优先级35          System.out.println(Thread.currentThread().getName()+"---"+Thread.currentThread().getPriority());36      }37  }

 

 

守护线程

  • 线程分为用户线程和守护线程(daemon)

  • 虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕

 1  public class TeatDaemon { 2      public static void main(String[] args) { 3          God god = new God(); 4          Person person = new Person(); 5   6          Thread thread = new Thread(god); 7          thread.setDaemon(true); //正常情况下默认为false,即用户线程,这里人为设置true 8   9          thread.start();10  11          new Thread(person).start();12      }13  }14  15  class God implements Runnable{16      @Override17      public void run() {18          while (true){19              System.out.println("God bless you");20          }21      }22  }23  class Person implements Runnable{24      @Override25      public void run() {26          for (int i = 0; i < 36500; i++) {27              System.out.println("alive");28          }29          System.out.println("end==============");30      }31  }

 

 

线程同步
  • 线程同步的条件:队列+锁

  • 锁机制synchronized:

    • 一个线程用有所会导致其他需要此锁的线程挂起

    • 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置

并发问题

 1  //多个线程同时操作一个对象(买火车票) 2  //多个线程同时操作同一个资源,出现数据紊乱 3  public class Demo04 implements Runnable{ 4   5      private int ticketNums = 10; 6      @Override 7      public void run() { 8          while (true){ 9              if (ticketNums <= 0) break;10              //线程sleep方法模拟延时11              try {12                  Thread.sleep(200);13              } catch (InterruptedException e) {14                  e.printStackTrace();15              }16              //Thread.currentThread().getName() 获取当前线程的名字17              System.out.println(Thread.currentThread().getName()+" got "+ticketNums--);18          }19      }20  21      public static void main(String[] args) {22          Demo04 demo04 = new Demo04();23  24          new Thread(demo04,"user1").start();25          new Thread(demo04,"user2").start();26          new Thread(demo04,"user3").start();27      }28  }

 

 

同步方法

  • synchronized关键字包括:synchronized方法和synchronized块

  • synchronized方法

    • synchronized方法控制“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞;方法一旦执行就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。

  • synchronized块

    • Obj称为同步监视器,推荐使用共享资源作为同步监视器

1  //synchronized同步方法,锁的是this,即类本身2  private synchronized void name(){}3  //synchronized块可以自定义锁的对象,锁的对象是变化的量(增删改的对象)4  synchronized (任何对象){}
  • JUC安全类型的集合:CopyOnWriteArrayList

 

  • ReentranLock类(可重入锁)实现了Lock,拥有和synchronized相同的并发性和内存语义,可以显式加锁.lock()、释放锁.unlock()

 1  //测试lock锁 2  public class TestLock { 3   4      public static void main(String[] args) { 5          TestLock2 testLock2 = new TestLock2(); 6   7          new Thread(testLock2).start(); 8          new Thread(testLock2).start(); 9          new Thread(testLock2).start();10      }11  12  }13  14  class TestLock2 implements Runnable{15  16      int ticketNums = 10;17  18      //定义lock锁19      private ReentrantLock lock = new ReentrantLock();20  21      @Override22      public void run() {23          while (true){24  25              try {26                  27                  //加锁28                  lock.lock();29  30                  if (ticketNums > 0){31                      try {32                          Thread.sleep(1000);33                      } catch (InterruptedException e) {34                          e.printStackTrace();35                      }36                      System.out.println(ticketNums--);37                  }else {38                      break;39                  }40              } catch (Exception e) {41                  e.printStackTrace();42              } finally {43  44                  //解锁45                  lock.unlock();46                  //解锁一般放在finally块中47              }48          }49      }50  }
 

synchronized和Lock的对比

  • Lock是显式锁,需要手动开关;synchronized是隐式锁,离开了作用域自动释放

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

  • Lock锁性能更好,并具有更好的扩展性

  • 优先顺序:Lock>同步代码块>同步方法

 

线程通信

生产者消费者问题

  • Java中几个解决线程通信问题的方法(只能在同步方法或同步代码块中使用,否则抛出异常)

    方法名作用
    wait()线程等待,会释放锁
    wait(long timeout)指定等待的毫秒数
    notify()唤醒一个处于等待状态的线程
    notifyAll()唤醒同一个对象上所有调用wait方法的线程,按优先级调度

管程法

 1  //管程法缓冲区解决生产者消费者问题 2  public class TestPC { 3      public static void main(String[] args) { 4          SynContainer synContainer = new SynContainer(); 5          new Productor(synContainer).start(); 6          new Consumer(synContainer).start(); 7      } 8  } 9  10  class Productor extends Thread{11      SynContainer container;12  13      public Productor(SynContainer container){14          this.container = container;15      }16      //生产17      @Override18      public void run() {19          for (int i = 0; i < 100; i++) {20              System.out.println(i+" producted");21              container.push(new Product(i));22          }       
23      }24  }25  26  class Consumer extends Thread{27      SynContainer container;28  29      public Consumer(SynContainer container){30          this.container = container;31      }32  33      //消费34      @Override35      public void run() {36          for (int i = 0; i < 100; i++) {37              System.out.println(container.pop().id+" consumed");38          }39      }40  }41  42  class Product {43      int id;44  45      public Product(int id) {46          this.id = id;47      }48  49  }50  //缓冲区51  class SynContainer{52      //容器大小53      Product[] products = new Product[10];54      //容器计数器55      int count = 0;56      //生产者放入产品57      public synchronized void push(Product product) {58          //如果容器满,等待59          if (count == 10) {60              //生产者等待61              try {62                  this.wait();63              } catch (InterruptedException e) {64                  e.printStackTrace();65              }66          }67          //如果容器未满,放入产品68          products[count] = product;69          count++;70          //通知消费者消费71          this.notifyAll();72      }73  74      //消费者消费产品75      public synchronized Product pop() {76          //判断能否消费77          if (count == 0) {78              //消费者等待79              try {80                  this.wait();81              } catch (InterruptedException e) {82                  // TODO Auto-generated catch block83                  e.printStackTrace();84              }85          }86          //如果可以消费87          count--;88          Product product = products[count];89          //通知生产者生产90          this.notifyAll();91          92          return product;93      }94  }

 

标志位法

 1  //信号灯法,通过标志位解决 2  public class TestPC2 { 3      public static void main(String[] args) { 4          TV tv = new TV(); 5          new Player(tv).start(); 6          new Audience(tv).start(); 7      } 8  } 9  10  //生产者---演员11  class Player extends Thread{12      TV tv;13      public Player(TV tv){14          this.tv = tv;15      }16      @Override17      public void run() {18          for (int i = 0; i < 20; i++) {19              this.tv.play("program"+i);20          }21      }22  }23  //消费者---观众24  class Audience extends Thread{25      TV tv;26      public Audience(TV tv){27          this.tv = tv;28      }29      @Override30      public void run() {31          for (int i = 0; i < 20; i++) {32              tv.watch();33          }34      }35  }36  //产品---节目37  class TV{38      String voice;39      //标志位flag40      boolean flag = true;41  42      //表演43      public synchronized void play(String voice){44          if (!flag){45              try {46                  this.wait();47              } catch (InterruptedException e) {48                  e.printStackTrace();49              }50          }51          System.out.println("演员表演了"+voice);52          //通知观众公关看53          this.notifyAll();54          this.voice = voice;55          this.flag = !this.flag;56      }57      //观看58      public synchronized void watch(){59          if (flag) {60              try {61                  this.wait();62              } catch (InterruptedException e) {63                  e.printStackTrace();64              }65          }66          System.out.println("观众观看了"+voice);67          //通知演员表演68          this.notifyAll();69          this.flag = !this.flag;70      }71  }

 

线程池

  • 提前创建多个线程,放入线程池,避免频繁创建和销毁,实现重复利用。

  • 使用线程池的优点:

    • 提高响应速度

    • 降低资源消耗

    • 便于线程管理

  • 使用线程池:

    • ExecutorService:线程池接口,通过Executors工具类创建并返回不同类型的线程池并执行Runnable

    • 也可以通过submit(Callable)执行,这种方法有返回值

 1  //线程池 2  public class TestPool { 3      public static void main(String[] args) { 4          //创建线程池 5          //newFixedThreadPool(线程池大小) 6          ExecutorService service = Executors.newFixedThreadPool(10); 7   8          //启动线程 9          service.execute(new MyThread());10          service.execute(new MyThread());11          service.execute(new MyThread());12          service.execute(new MyThread());13  14          //关闭线程池15          service.shutdown();16      }17  }18  19  class MyThread implements Runnable{20      @Override21      public void run() {22              System.out.println(Thread.currentThread().getName());23      }24  }25