Java多线程

线程实现

  • 三种创建线程的方式:

    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      @Override
12      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 target
20          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      @Override
15      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 target
24          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      @Override
23      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      @Override
37      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              @Override
22              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              @Override
32              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      @Override
54      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 may
11       * 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     @Override
11     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      @Override
12      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方法,线程处于创建态NEW
17 
18         thread.start();    //调用start后,进程进入就绪态,准备运行
19         state = thread.getState();
20         System.out.println(state);  //此时线程为运行态RUNNABLE
21 
22         while (state != Thread.State.TERMINATED){   //只要线程不终止TERMINATED,就一直输出状态
23             state = thread.getState();
24             System.out.println(state);  //5s内线程处于就绪等待状态TIMED_WAITING
25         }
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);//最大优先级10
23          t4.start();
24          t5.setPriority(Thread.MIN_PRIORITY);//最小优先级1
25          t5.start();
26          t6.setPriority(Thread.NORM_PRIORITY);//中等优先级5
27          t6.start();
28      }
29  }
30 31  class MyPriority implements Runnable{
32      @Override
33      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      @Override
17      public void run() {
18          while (true){
19              System.out.println("God bless you");
20          }
21      }
22  }
23  class Person implements Runnable{
24      @Override
25      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      @Override
22      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      @Override
18      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      @Override
35      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 block
83                  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      @Override
17      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      @Override
30      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      //标志位flag
40      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      @Override
21      public void run() {
22              System.out.println(Thread.currentThread().getName());
23      }
24  }
25

 

 

 

 

posted @ 2021-03-17 00:37  Colin13  阅读(41)  评论(0编辑  收藏  举报