zoukankan      html  css  js  c++  java
  • 七、多线程

    一、概念:

    1.程序、进程、线程

    程序:一段静态代码块;

    进程:正在运行的程序;系统会为每个进程分配内存空间
    线程:程序内部的一条执行路径,若一个进程同一时间并行执行多个线程,就是支持多线程;
       一个进程中的多个线程共享同一个内存单元/内存地址空间(他们从同一堆中分配对象,可以访问相同的变量和对象(因为线程之间共享方法去和堆),就使得线程通信更简便高效,注意线程之间共享方法去和堆,其他不共享),但是多个线程共享的系统资源可能会带来安全隐患;

    2.单核CPU和多核cup;

      单核CPU的多线程:一个假的多线程,在一个单位时间内,也只能执行一个线程任务。例如收费站收费的工作人员,虽然多个车道,但是只有一个收费人员,当有车不想缴费时,工作人员挂起该车辆,同时给其他车辆做收费;这么个道理;
     
      多核CPU:才能更好的发挥多线程的效率; 一个java应用程序,至少有3个线程:main() 主线程和gc()和异常;
     
    使用多线程的优点;以单核为例;
      如果 需要c盘文件 复制到 D盘 e盘文件复制到 f盘 方式一、先复制c->d 然后 e->f 方式二:c->d 同时
    e->f 此时 方式一更快;

      如果是多核cpu,那么方式二更快;因为单核CPU执行多个线程,则需要切换不同的线程需要花费时间;二多核cpu呢,他不需要切换线程,则方式二就更快了;   

    3.什么时候需要用到多线程:

    a.程序需要同时执行两个或者多个任务, b..程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等;  c.需要一些后台运行的程序

    4.并行 和 并发 并行:

    多个CPU同时执行多个任务,多个人做不同的事情; 并发:一个CPU同时指向多个任务;

    二、创建多线程的4中方式

    1、继承Thread方式:

    a1.创建一个继承Thread的子类,

    a2.重写run方法;

    a3.创建Thread子类的对象,通过此对象 调用start方法;start的作用:使得线程begin,调用当前线程的run方法;

    例1:

     1 ///1.创建一个继承 Thread的子类;
     2 class SubThread1 extends Thread {
     3     // 2.重写Thread类的run方法,方法内实现此子线程要完成的功能
     4     public void run() {
     5         for (int i = 1; i < 101; i++) {
     6             try {
     7                 if(i%2 ==0) {
     8                     System.out.println(Thread.currentThread().getName() + ":" + i);
     9                 }
    10                 Thread.currentThread().sleep(1000);//休息一秒钟
    11             } catch (InterruptedException e) {
    12                 // TODO Auto-generated catch block
    13                 e.printStackTrace();
    14             }
    15             
    16         }
    17     }
    18 }
    19 
    20 public class Day18CreateThreadByExtendsThread13 {
    21     public static void main(String[] args) {
    22 
    23         
    24 //        
    25         //3.创建一个子类的对象
    26         SubThread1 st = new SubThread1();
    27         //4.调用线程的start方法,启动此线程;再调用run方法(一个线程只能执行一次start(),可以创建两个对象来分别执行start方法!)
    28         st.start();//start 使得子线程begin 然后执行run方法;
    29         //再启动一个线程,则需要重新创建一个子类对象,再执行start方法。
    30         SubThread1 st1= new SubThread1();
    31         st1.start();
    32         System.out.println(Thread.currentThread().getName()+":hello!");//主线程内打印“hello”
    33     }
    34     
    35     
    36 }

    2、实现runnable 接口方式

    b1.创建一个实现Runnable接口的类,

    b2.重写run方法;

    ...

    例2:

     1 public class Day18CreateThreadByRunnableInterface13 {
     2 
     3     public static void main(String[] args) {
     4     //3创建一个Runnable接口实现类的对象
     5         PrintNum2 pn = new PrintNum2();
     6     //要想启动一个多线程,必须调用start
     7     //4将实现类对象作为形参传递给Thread类的构造器中,创建Thread类的对象,此对象即为一个线程
     8             Thread t1 = new Thread(pn);
     9     //启动线程,执行Thread对象生成时构造器形参的对象
    10     //5调动start,启动线程执行run方法
    11             t1.start();
    12     //再创建一个线程
    13             Thread t2 = new Thread(pn);
    14             t2.start();
    15     }
    16 }
    17 
    18 //1创建一个实现了Runnable接口类
    19 class PrintNum2 implements Runnable {
    20 //2实现接口的抽象方法
    21     public void run() {
    22         for (int i = 1; i < 101; i++) {
    23             System.out.println(Thread.currentThread().getName() + ":" + i);
    24         }
    25     }
    26 }

    3.实现callable接口方式

    c1.创建一个实现Runnable接口的类,

    c2.重写call方法;

    c3.创建 callable接口实现类的对象

    c4.将callable 接口实现类的对象 放入 Future中

    c5.将futuretask 放入 Thread构造器中,,创建thread对象,并启动线程

    c6.获取返回值;

      如何理解理解实现callable接口方式创建多线程比实现runnable接口创建对现场方式强大?
      1.call方法可以有返回值;
      2.call方法可以抛出异常,别外面的操作捕获;
      3.支持泛型

    例3

     1 public class Day19CreatThreadByCallable18 {
     2     public static void main(String[] args) {
     3         //3创建 callable接口实现类的对象
     4         NumThread uumThread = new NumThread();
     5         //4.将callable 接口实现类的对象 放入 Future中
     6         FutureTask futureTask = new FutureTask(uumThread);
     7         //5.将futuretask 放入 Thread构造器中,,创建thread对象,并启动线程
     8         Thread thread  = new Thread(futureTask);
     9         thread.start();
    10         try {
    11             //6.获取返回值;
    12             //get方法的返回值,即为futuretask 构造器参数callable实现类重写的call()的返回值
    13             int sum =  (int)futureTask.get();
    14             System.out.println(sum);
    15         } catch (InterruptedException e) {
    16             // TODO Auto-generated catch block
    17             e.printStackTrace();
    18         } catch (ExecutionException e) {
    19             // TODO Auto-generated catch block
    20             e.printStackTrace();
    21         }
    22     }
    23 }
    24 //1.创建一个实现callable的实现了
    25 class NumThread implements Callable{
    26     //遍历100以内的偶数,并且返回和
    27     //2.重写 Call方法,将此线程需要执行的操作声明在call()中,同事call()方法可以有返回值
    28     @Override
    29     public Object call() throws Exception {
    30         int sum = 0;
    31         for(int i=1;i<=100;i++) {
    32             if(i%2 == 0) {
    33                 sum = sum+i;
    34             }
    35         }
    36         return sum;
    37     }
    38 }

    4.使用线程池方式创建多线程;

    d1.提供指定数量线程的线程池

    d2.创建实现runnable接口的实现类 or 创建实现callable接口的实现类;

    d3..执行指定线程的操作;

    d4.关闭线程池

    使用线程池:
      背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大;
      思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用。类似生活中的公共交通工具;
      好处:
          提前响应速度、降低资源消耗、便于线程管理(
      corePoolSize :核心池的大小;
      MaximumPoolSize:最大线程数;
      keepAliveTime:线程没有任务时最多保持多长后会终止;
      );

    例4:

     1 public class Day19CreatThreadByPool19 {
     2     public static void main(String[] args) {
     3         //1.提供指定数量线程的线程池
     4         ExecutorService service = Executors.newFixedThreadPool(10);
     5         //其他:设置线程池的属性:
     6         ThreadPoolExecutor service1 = (ThreadPoolExecutor)service;
     7 //        service1.setCorePoolSize(15);
     8         //2.创建实现runnable接口的实现类 or 创建实现callable接口的实现类
     9         NumberThread1 numThread1 = new NumberThread1();
    10         NumberThread2 numberThread2 = new NumberThread2();
    11         FutureTask  futureTask = new FutureTask(numberThread2);
    12         //3.执行指定线程的操作;
    13         service.execute(numThread1);//适合使用runnable
    14         service.submit(futureTask);//适合使用 callable方法
    15         //4.关闭连接池
    16         service.shutdown();//关闭连接池
    17     }
    18 }
    19 class NumberThread1 implements Runnable{
    20     @Override
    21     public void run() {
    22         int sum = 0;
    23         for(int i = 1;i<=100;i++) {
    24             if(i%2 == 0) {
    25                 sum = sum +i;
    26                 System.out.println(Thread.currentThread().getName()+":"+i);
    27             }
    28             
    29         }
    30         System.out.println("runnabl sum:"+sum);
    31         
    32     }
    33     
    34 }
    35 
    36 class NumberThread2 implements Callable{
    37     @Override
    38     public Object call() throws Exception {
    39         int sum = 0;
    40         for(int i = 1;i<=100;i++) {
    41             if(i%2 == 0) {
    42                 sum = sum +i;
    43                 
    44             }else {
    45                 System.out.println(Thread.currentThread().getName()+":"+i);
    46             }
    47             
    48         }
    49         System.out.println("callable sum:"+sum);
    50         return sum;
    51     }
    52     
    53 }

    三、Thread的常用方法

     1.start()方法:1.begin 线程 ,2.调用当前线程的run方法;
     2.run()方法:需要重写Thread类中的此方法,将创建的线程哟啊执行的操作声明在此方法中
     3.currentThread();返回一个Threan 当前执行代码的线程;
     4.getName();获取当前线程的名称
     5.setName():设置当前线程的名字 注意在 start前面
     6.yield():释放当前cpu的执行权
     7.join();在线程a中调用线程b的join(),此时a就进入了阻塞状态,知道b完全指向完以后,a才结束阻塞状态;
     8.stop();强制结束当前线程;已过期
     9.sleep();让当前线程“睡眠”指定的millitime毫秒,在指定的millitime毫秒时间内,当前线程是阻塞状态;
     10.isAlive():判断当前线程是否存活;当线程执行完成后,则线程死亡;

     1 public class Day18ThreadMethods {
     2 
     3     public static void main(String[] args) {
     4         ThreadTest1 test1 = new ThreadTest1();
     5         ThreadMethods2 test2 = new ThreadMethods2("线程2");
     6         test1.setName("线程1");
     7         test1.start();
     8         test2.start();
     9         for(int i=0;i<100;i++) {
    10             if(i%10 == 0) {
    11                 System.out.println(Thread.currentThread().getName()+":"+i);
    12             }
    13             if(i==20) {
    14                 try {
    15                     test1.join();
    16                 } catch (InterruptedException e) {
    17                     // TODO Auto-generated catch block
    18                     e.printStackTrace();
    19                 }
    20             }
    21             System.out.println(test1.isAlive());
    22             
    23         }
    24         
    25     }
    26 }
    27 
    28 class ThreadMethods1 extends Thread{
    29     @Override
    30     public void run() {
    31         try {
    32             sleep(1000);//阻塞一秒;sleep在此时只能使用 try catch,why?因为run的父类方法没有抛异常,所以子类就不能抛异常
    33         } catch (InterruptedException e) {
    34             // TODO Auto-generated catch block
    35             e.printStackTrace();
    36         }//睡眠1秒
    37         // TODO Auto-generated method stub
    38         for(int i=0;i<100;i++) {
    39             if(i%2 == 0) {
    40                 System.out.println(Thread.currentThread().getName()+":"+i);
    41             }
    42             if(i%20 == 0) {
    43                 yield();
    44             }
    45             
    46         }
    47     }
    48     public ThreadMethods1(String name) {
    49         super(name);
    50     }
    51 }
    52 class ThreadMethods2 extends Thread{
    53     @Override
    54     public void run() {
    55         // TODO Auto-generated method stub
    56         for(int i=0;i<100;i++) {
    57             if(i%2 != 0) {
    58                 System.out.println(Thread.currentThread().getName()+":"+i);
    59             }
    60             
    61         }
    62     }
    63     public ThreadMethods2(String name) {
    64         super(name);
    65     }
    66 }
    View Code

    四、线程的调度


    时间片
     抢占式:高优先级的线程抢占cpu
     java的调度方法:
     同优先级的组成先进先出队列(先到先服务),使用时间片服务;
     对高优先级,使用优先调度的抢占式策略;
     
     线程的优先等级:
     MAX_PRIORITY:10
     MIN_PRIORITY:1;
     NORM_PRIORITY:5
     方法:
     getPriority():返回线程的优先等级值
     setPriority():设置线程的优先等级值;
     
     说明:高优先级的线程要抢占低优先级CPU执行次序,但是只是从概率上讲,高概率上执行,并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行

     1 public class Day18Priority17 {
     2 public static void main(String[] args) {
     3     System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().getPriority());//获取主线程的优先等级值 5
     4     ThreadPriority test = new ThreadPriority();
     5     test.setPriority(10);//设置分线程的优先级
     6     test.start();
     7     for(int i=0;i<100;i++) {
     8         
     9         System.out.println(Thread.currentThread().getName()+":"+i);
    10     }
    11 }
    12 }
    13 
    14 class ThreadPriority extends Thread{
    15     @Override
    16     public void run() {
    17         for(int i=0;i<100;i++) {
    18             System.out.println(Thread.currentThread().getName()+":"+i);
    19         }
    20         super.run();
    21     }
    View Code

    五、线程安全

    1.线程安全 产生背景:

    例5场景是,3个窗口同时卖火车票(共100张);出现的问题是:不同窗口可能出现相同的票号;可能会卖出错票(即出现了-1号票等情况):

    原因:当某线程操作车票时,操作尚未完成,其他线程参与进来,也操作车票导致;

    解决:当一个线程在操作ticket,其他线程不能参与进来,知道该线程操作完了,其他线程才可以开始操作ticket,这种情况即使该线程出现阻塞,也不能被改变;
    在java中,我们通过同步机制来解决线程的安全问题;

    例5:

     1 /
     2   例子:创建3个买票窗口卖票
     3   @Description        
     4 @author            lixiuming
     5 @version    
     6   @date            2020年12月22日下午7:43:25            
     7  创建线程的两种方式的比较:
     8  开发中优先选择实现runnable接口;
     9  原因:实现方式没有类的单继承的局限性
    10          实现方式更实现多个线程的有共享数据的情况
    11  联系:Thread也实现了runnable接口
    12  两种方式都需要重写run方法,将逻辑代码都写在run()方法中;
    13  java 程序至少有3个线程;一个是主线程 一个是gc()垃圾回收线程,异常处理线程;
    14  /
    15 public class Day18WindowTest18 {
    16     public static void main(String[] args) {
    17 //        Window window1 = new Window();
    18 //        Window window2 = new Window();
    19 //        Window window3 = new Window();
    20 //        window1.start();
    21 //        window2.start();
    22 //        window3.start();
    23         WindowbyRunnable windowbyRunnable = new WindowbyRunnable();
    24         Thread wr1 = new Thread(windowbyRunnable);
    25         Thread wr2 = new Thread(windowbyRunnable);
    26         Thread wr3 = new Thread(windowbyRunnable);
    27         wr1.start();
    28         wr2.start();
    29         wr3.start();
    30         
    31                 
    32     }
    33 }
    34 
    35 
    36 //继承Thread 方式实现 窗口卖票
    37 class Window extends Thread{
    38     private static int ticket = 100;
    39     @Override
    40     public void run() {
    41         // TODO Auto-generated method stub
    42         while(true) {
    43             if(ticket>0) {
    44                 System.out.println(Thread.currentThread().getName()+":"+ticket);
    45                 ticket--;
    46             }else {
    47                 break;
    48             }
    49         }
    50     }}
    51 
    52 //使用runnable接口方式实现窗口
    53     class WindowbyRunnable implements Runnable{
    54         private  int ticket = 100;
    55         @Override
    56         public void run(){
    57             // TODO Auto-generated method stub
    58             while(true) {
    59                 if(ticket>0) {
    60                     System.out.println(Thread.currentThread().getName()+":"+ticket);
    61                     ticket--;
    62                 }else {
    63                     break;
    64                 }
    65         }
    66         
    67     }    
    68     }

     2.同步块|同步方法 解决线程安全:

    方式一:同步代码块 1.synchronized(同步监视器){ 需要被同步的代码 }; 说明:
    1.操作共享数据的代码就是 需要被同步的代码;不能包含代码多了,也不能包含少了;

    2.共享数据:多个线程操作的变量;比如本问题的ticket
    3.同步监视器:俗称锁;任何类的对象都可以充当锁;(一定保证所有线程共用一个锁,也就是说作为锁的类只能保证只被创建过一次)
    补充:在实现runnable接口创建多线程方式中,可以考虑使用this 来充当同步监视器;

    说明,在继承Thread创建多线程方式中,慎用this充当,可以考虑当前类来充当同步监视器;

    方式二:同步方法 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的;

     说明:
      使用同步方法来解决实现runnable接口的线程安全问题;
      关于同步方法的总结:
      1.同步方法仍然涉及到同步监视器,只是不需要我们现实的声明。
      2.非静态的同步方法:同步监视器是this;
          静态的同步方法:同步监视器是当前类本身;

    例6(同步代码块方式)

     1 public class Day19ThreadSafe6 {
     2     public static void main(String[] args) {
     3         WindowOfSaleTicket windowOfSaleTicket = new WindowOfSaleTicket();
     4         Thread thread1 = new Thread(windowOfSaleTicket);
     5         Thread thread2 = new Thread(windowOfSaleTicket);
     6         Thread thread3 = new Thread(windowOfSaleTicket);
     7         thread1.start();
     8         thread2.start();
     9         thread3.start();
    10     }
    11 }
    12 
    13 class WindowOfSaleTicket implements Runnable {
    14     private int ticketcount = 100;
    15     Object obj = new Object();
    16 
    17     @Override
    18     public void run() {
    19 
    20         while (true) {
    21             synchronized (obj) {// obj 可以用 this来充当,this代表当前对象,即windowOfSaleTicket
    22                 if (ticketcount > 0) {
    23                     try {
    24                         Thread.sleep(100);
    25                         System.out.println(Thread.currentThread().getName() + ":" + ticketcount);
    26                         ticketcount--;
    27                     } catch (InterruptedException e) {
    28                         // TODO Auto-generated catch block
    29                         e.printStackTrace();
    30                     }
    31                 } else {
    32 
    33                     break;
    34                 }
    35 
    36             }
    37         }
    38 
    39     }
    40 }

    例7(同步方法:)

     1 public class Day19ThreadSafe8 {
     2 public static void main(String[] args) {
     3     WindowOfSaleTicket1 windowOfSaleTicket = new WindowOfSaleTicket1();
     4     Thread thread1 = new Thread(windowOfSaleTicket);
     5     Thread thread2 = new Thread(windowOfSaleTicket);
     6     Thread thread3 = new Thread(windowOfSaleTicket);
     7     thread1.start();
     8     thread2.start();
     9     thread3.start();
    10 }
    11 }
    12 
    13 class WindowOfSaleTicket1 implements Runnable{
    14     private int ticketcount = 100;
    15     Object obj = new Object();
    16     @Override
    17     public  void run() {
    18         while (true) {
    19             show();
    20         }    
    21         
    22     }
    23     
    24     public synchronized void show() {
    25         if (ticketcount > 0) {
    26             try {
    27                 Thread.sleep(100);
    28                 System.out.println(Thread.currentThread().getName() + ":" + ticketcount);
    29                 ticketcount--;
    30             } catch (InterruptedException e) {
    31                 // TODO Auto-generated catch block
    32                 e.printStackTrace();
    33             }
    34         } 
    35     }
    36     
    37 }

    3.使用Lock方式解决线程安全问题:

    解决线程安全问题的方式三:Lock锁,JDK5.0新增的方式;
    面试题:synchronized 与Lock的异同?
    同:二者都是解决线程安全的;
    不同:synchronized 在执行完代码后,自动释放同步监视器,Lock需要手动的锁定和手动的解锁;

    线程安全的解决方式:
    1.同步sychronized :同步代码块 和 同步方法;
    2.Lock的方式

    例8:

     1 public class Day19CreateThreadSafeLock13 {
     2 public static void main(String[] args) {
     3     WindowOfSaleTicket2 windowOfSaleTicket = new WindowOfSaleTicket2();
     4     Thread thread1 = new Thread(windowOfSaleTicket);
     5     Thread thread2 = new Thread(windowOfSaleTicket);
     6     Thread thread3 = new Thread(windowOfSaleTicket);
     7     thread1.start();
     8     thread2.start();
     9     thread3.start();
    10 }
    11 }
    12 
    13 class WindowOfSaleTicket2 implements Runnable{
    14     //实例化Lock
    15     private ReentrantLock lock = new ReentrantLock();
    16     
    17     
    18     
    19     private int ticketcount = 100;
    20     Object obj = new Object();
    21     @Override
    22     public  void run() {
    23         while (true) {
    24             try {
    25                 
    26                 //调用lock方法 锁定
    27                 lock.lock();
    28                 show();
    29                 
    30             }finally {
    31                 //解锁
    32                 lock.unlock();
    33             }
    34             
    35         }    
    36         
    37     }
    38     
    39     public  void show() {
    40         if (ticketcount > 0) {
    41             try {
    42                 Thread.sleep(100);
    43                 System.out.println(Thread.currentThread().getName() + ":" + ticketcount);
    44                 ticketcount--;
    45             } catch (InterruptedException e) {
    46                 // TODO Auto-generated catch block
    47                 e.printStackTrace();
    48             }
    49         } 
    50     }
    51     
    52 }

    4.线程安全使用案例:

    有两个储户分别向一个账户存3000元,每次存1000,存3次,每次存完打印账户余额;

     1 public class Day19ThreadQuestion14 {
     2 public static void main(String[] args) {
     3     BankCount bankCount = new BankCount();
     4     Thread thread1 = new Thread(bankCount);
     5     Thread thread2 = new Thread(bankCount);
     6     thread1.setName("甲");
     7     thread2.setName("乙");
     8     thread1.start();
     9     thread2.start();
    10 }
    11 }
    12 class BankCount implements Runnable{
    13     double account = 0;
    14     int count = 0;
    15     @Override
    16     public void run() {
    17         while(true) {
    18             try {
    19                 Thread.sleep(200);
    20             } catch (InterruptedException e) {
    21                 // TODO Auto-generated catch block
    22                 e.printStackTrace();
    23             }
    24             addAccount(1000);
    25             
    26             
    27         }
    28         
    29         
    30     }
    31     private synchronized double addAccount(double mony) {
    32         if(count<3) {
    33             account += mony;
    34             System.out.println(Thread.currentThread().getName()+"存完后的余额:"+account+",count:"+count);
    35             
    36             count++;
    37             
    38         }
    39         
    40         return account;
    41     }
    42     
    43 }
    View Code

    5.线程安全的单例模式

     1 public class Bank{
     2   private Bank(){
     3   }
     4   public static Bank instance = null;
     5   public static Band getInstance(){
     6      if(instance == null){
     7       synchronized(Bank.class){
     8          instance = new Bank();
     9       }
    10     }       
    11         return  instance;
    12   }
    13 }    

    六、线程的生命周期:

      1.创建;Thread类 or 子类的对象声明并创建时,新生线程对象处于新建状态 MyThread thread = new MyThread();
      2.就绪;调用start thread.start();
      3.运行;就绪的线程被CPU调度并获得cpu资源,那么就是进入了运行状态;
      4.阻塞;让出CPU并临时中断自己的执行,就是进入了阻塞状态;
      5.死亡;线程完成了它的全部工作或被强制终止or遇到异常导致结束;

    七、线程的通信:

    涉及到的3个方法:
      1.wait():一旦执行此方法,当前线程就处于阻塞状态;并释放同步监视器
      2.notify():一旦执行此方法:就会唤醒的一个被wait()的方法,如果有多个线程被wait(),就唤醒优先级高的;
      3.notifyAll():一旦执行此方法:就会唤醒的所有被wait()的方法,
      说明:
      1.线程通信这3个方法,都得在同步代码块或者同步方法中,;
      2.线程通信这3个方法调用者必须是同步代码块或者是同步方法中的同步监视器,否则出异常;
      3.线程通信这3个方法,定义在object类中的 java.long.object
     线程通信例子:打印1-100,线程1 线程二 交替打印;

     1 public class Day19ThreadCommunication15 {
     2 public static void main(String[] args) {
     3     Communication1 communication1 = new Communication1();
     4     Thread thread1 = new Thread(communication1);
     5     Thread thread2 = new Thread(communication1);
     6     thread1.setName("甲");
     7     thread2.setName("乙");
     8     thread1.start();
     9     thread2.start();
    10 }
    11 }
    12 class Communication1 implements Runnable{
    13     int count = 1;
    14     @Override
    15     public void run() {
    16         while(true) {
    17             try {
    18                 Thread.sleep(200);
    19             } catch (InterruptedException e) {
    20                 // TODO Auto-generated catch block
    21                 e.printStackTrace();
    22             }
    23             printNum();
    24             
    25             
    26         }
    27         
    28         
    29     }
    30     private synchronized void printNum() {
    31         
    32         if(count<101) {
    33                 System.out.println(Thread.currentThread().getName()+":"+count);
    34                 count++;
    35                 notify();
    36                 try {
    37                     wait();
    38                 } catch (InterruptedException e) {
    39                     // TODO Auto-generated catch block
    40                     e.printStackTrace();
    41                 }            
    42             
    43             
    44         }
    45         
    46     }
    47     
    48 }

      面试题:
      sleep 和 wait的异同
      同:都会使得当前线程进入阻塞状态;
      异:.
      1.两个方法定义的位置不同;Thread 类中声明sleep();object中声明了wait();
      2.调用的范围不同,sleep()可以在任何需要的位置调用,wait()必须在同步代码块or方法中;
      3.如果两个方法都使用在同步代码块or同步方法中,sleep不会是否同步监视器;wait()会释放同步监视器

    八、死锁:

      使用同步机制改写单例模式的懒汉式,改写为线程安全
      1.死锁的理解:不同的线程 占用 对方的需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源就形成了死锁
      2.说明:出现死锁后,不会抛出异常,不会提示,只是所有的线程都处于阻塞状态;
      我们使用同步时,要避免死锁;
      解决方法:
      1.专门的算法、原则
      2.尽量减少同步资源的定义;
      3.尽量避免嵌套同步方法;

    九、综合例子:

      面试题:
      生产者(Productor)将产品交到店员(clerk),而消费者(Customer)从店员处取走产品,店员一次只能持有
      固定数量的产品(比如20),如果生产者视图生产更多的产品,店员就好叫生产者停一下,如果点店中有了空位放产品了
      在通知生产者继续生产,如果店中没有产品了,店员会告诉消费者等一下,如果店中有产品了在通知消费者取走产品;
      分析:
      是否是多线程问题:生产者线程;消费者线程
      是否有线程安全问题:共享数据是 ”产品数“
      如何解决安全问题,synchronize的两张方法 or lock;
      是否需要线程的通信 :是

     1 public class Day19ThreadQuestionProductorAndConsumer17 {
     2 public static void main(String[] args) {
     3     ProductorAndConsumer productorAndConsumer = new ProductorAndConsumer();
     4     Thread Productor = new Thread(productorAndConsumer);
     5     Thread Comsumer = new Thread(productorAndConsumer);
     6     Productor.setName("生产者");
     7     Comsumer.setName("消费者");
     8     Productor.start();
     9     Comsumer.start();
    10 }
    11 }
    12 class ProductorAndConsumer implements Runnable{
    13     int account = 0;
    14     @Override
    15     public void run() {
    16         while(true) {
    17             try {
    18                 Thread.sleep(2000);
    19             } catch (InterruptedException e) {
    20                 // TODO Auto-generated catch block
    21                 e.printStackTrace();
    22             }
    23             addAccount();
    24             
    25             
    26         }
    27         
    28         
    29     }
    30     private synchronized double addAccount() {
    31         notify();
    32         String currentName = Thread.currentThread().getName();
    33         if(account==0) {
    34             
    35             if(currentName.equals("消费者")) {
    36                 try {
    37                     System.out.println("account:"+account+",消费者阻塞");
    38                     wait();
    39                 } catch (InterruptedException e) {
    40                     // TODO Auto-generated catch block
    41                     e.printStackTrace();
    42                 }
    43             }else {
    44                 account = account+1;
    45                 System.out.println("account:"+account+",生产者生产第:"+account+"个产品!");
    46                 
    47             }
    48             
    49         }else if(account==20) {
    50             if(currentName.equals("生产者")) {
    51                 try {
    52                     System.out.println("account:"+account+",生产者阻塞");
    53                     wait();
    54                 } catch (InterruptedException e) {
    55                     // TODO Auto-generated catch block
    56                     e.printStackTrace();
    57                 }
    58                 
    59             }else {
    60                 account = account-1;
    61                 System.out.println("account:"+account+",消费者消费第:"+account+"个产品!");
    62                 
    63             }
    64             
    65         }else {
    66             if(currentName.equals("生产者")) {
    67                 account++;
    68                 System.out.println("account:"+account+",生产者生产第:"+account+"个产品!");
    69                 
    70                 
    71             }else {
    72                 
    73                 System.out.println("account:"+account+",消费者消费第:"+account+"个产品!");
    74                 account--;
    75             }
    76             
    77         }
    78         
    79         
    80         return account;
    81     }
    82     
    83 }
    View Code
      1 public class Day19ThreadQuestionProductorAndConsumer17_2 {
      2 public static void main(String[] args) {
      3     Clerk clerk = new Clerk();
      4     Productor productorAndConsumer = new Productor(clerk);
      5     Consummer comsumer = new Consummer(clerk);
      6     Thread Productor = new Thread(productorAndConsumer);
      7     Thread Comsumer = new Thread(comsumer);
      8     Productor.setName("生产者");
      9     Comsumer.setName("消费者");
     10     Productor.start();
     11     Comsumer.start();
     12 }
     13 }
     14 class Productor implements Runnable{
     15     private Clerk clert;
     16     
     17     public Productor(Clerk clert) {
     18         this.clert = clert;
     19     }
     20 
     21     @Override
     22     public void run() {
     23         while(true) {
     24             try {
     25                 Thread.sleep(2000);
     26             } catch (InterruptedException e) {
     27                 // TODO Auto-generated catch block
     28                 e.printStackTrace();
     29             }
     30             clert.addAccount();
     31         }
     32         
     33         
     34     }
     35     
     36 }
     37 class Consummer implements Runnable{
     38     private Clerk clerk ;
     39     
     40     public Consummer(Clerk clerk) {
     41         this.clerk = clerk;
     42     }
     43 
     44     @Override
     45     public void run() {
     46         while(true) {
     47             try {
     48                 Thread.sleep(2000);
     49             } catch (InterruptedException e) {
     50                 // TODO Auto-generated catch block
     51                 e.printStackTrace();
     52             }
     53             clerk.addAccount();
     54             
     55             
     56         }
     57         
     58         
     59     }
     60     
     61 }
     62 class Clerk{
     63     private int account=0;    
     64 
     65     public synchronized double addAccount() {
     66         notify();
     67         String currentName = Thread.currentThread().getName();
     68         if(account==0) {
     69             
     70             if(currentName.equals("消费者")) {
     71                 try {
     72                     System.out.println("account:"+account+",消费者阻塞");
     73                     wait();
     74                 } catch (InterruptedException e) {
     75                     // TODO Auto-generated catch block
     76                     e.printStackTrace();
     77                 }
     78             }else {
     79                 account = account+1;
     80                 System.out.println("account:"+account+",生产者生产第:"+account+"个产品!");
     81                 
     82             }
     83             
     84         }else if(account==20) {
     85             if(currentName.equals("生产者")) {
     86                 try {
     87                     System.out.println("account:"+account+",生产者阻塞");
     88                     wait();
     89                 } catch (InterruptedException e) {
     90                     // TODO Auto-generated catch block
     91                     e.printStackTrace();
     92                 }
     93                 
     94             }else {
     95                 account = account-1;
     96                 System.out.println("account:"+account+",消费者消费第:"+account+"个产品!");
     97                 
     98             }
     99             
    100         }else {
    101             if(currentName.equals("生产者")) {
    102                 account++;
    103                 System.out.println("account:"+account+",生产者生产第:"+account+"个产品!");
    104                 
    105                 
    106             }else {
    107                 
    108                 System.out.println("account:"+account+",消费者消费第:"+account+"个产品!");
    109                 account--;
    110             }
    111             
    112         }
    113         
    114         
    115         return account;
    116     }
    117     
    118 }
    View Code
    我从来不相信什么懒洋洋的自由。我向往的自由是通过勤奋和努力实现的更广阔的人生。 我要做一个自由又自律的人,靠势必实现的决心认真地活着。
  • 相关阅读:
    JSP九大内置对象及四个作用域
    JSP九大隐式对象
    8-5接口测试用例设计与编写-3发送消息与图片
    8-5接口测试用例设计与编写2 rest-assured
    8-5接口测试用例设计与编写1
    7-29接口测试入门
    校园商铺-4店铺注册功能模块-11店铺类别区域信息的获取
    校园商铺-4店铺注册功能模块-10店铺注册之js实现
    校园商铺-4店铺注册功能模块-9店铺注册之前端设计
    校园商铺-4店铺注册功能模块-8店铺注册之Controller层的改造
  • 原文地址:https://www.cnblogs.com/lixiuming521125/p/14289040.html
Copyright © 2011-2022 走看看