• Java多线程


    本节内容总结来自传智播客毕向东老师的公开课,感谢毕向东老师 !如有错误之处,欢迎大家指教 !

    创建线程的两种方法:

     1.继承Thread类:

         继承Thread类;

         重写run方法;

         子类创建对象(就是创建线程);

         对象调用start()方法,两个作用:启动线程,调用run()方法;

      2.实现Runnable接口:

         实现Runnable接口;

      重写run方法;

        子类创建对象(此时仅是创建对象,而不是创建线程);

        通过Thread类创建线程对象,并将实现Runnable接口子类对象作为参数放到构造函数中(创建线程);

        Thread类对象调用start()方法,启动线程,并运行子类对象的run()方法;

    两个比较:

      实现Runnnable接口方式更好;原因:1.java是只支持单继承,所以除了可以实现接口的方式之外还可以继承自其它的类; 2.能够共享数据;

    为什么要重写run()方法:

       Thread类用于描述线程,线程需要执行一些代码实现某个功能,这些实现具体功能的代码就在Thread类的run()方法里面定义;主线程在main()方法中存放实现具体功能的代码;所以run()被称为线程体

    多线程出现的安全问题:

       多条语句操作多个线程共享的资源时,一个线程只执行了部分语句,还没执行完,另一个线程又进来操作共享数据(执行语句),导致共享数据错误;

    解决思想:

      只有当一个线程执行完所有语句(操作共享资源的所有语句)之后,才能让另外一个线程进来再进行操作;

    具体的两种解决方法:

      同步代码块:

    synchronized(对象)
    {
        需要被同步的代码块;
    }

      同步函数:

      就是把需要同步的代码块放到一个函数里面,在函数上加 synchronized 关键字;

    public synchronized void show()
    {
        需要被同步的代码块;  
    }

    线程的几种状态及转换:

    一些实例:

     1 /**
     2  * 创建线程的第1种方法:继承Thread类
     3  */
     4 package Thread;
     5 
     6 public class ThreadDemo
     7 {
     8     public static void main(String[] args)
     9     {
    10         // 3.子类创建对象,就是创建线程,但还没启动
    11         MyThread thread = new MyThread();
    12         // 4.调用start()方法,两个作用:
    13         // 启动线程
    14         // 调用run()方法
    15         thread.start();
    16     }
    17 }
    18 
    19 // 1.继承Thread类
    20 class MyThread extends Thread
    21 {
    22     @Override  // 2.重写run()方法
    23     public void run()
    24     {
    25         System.out.println("MyThread run");
    26     }
    27 }
     1 /**
     2  * 创建两个线程,和主线程交互运行
     3  * 进程中至少有一个线程,就是主线程,线程体是main()
     4  */
     5 package Thread;
     6 
     7 public class ThreadDemo2
     8 {
     9     public static void main(String[] args)
    10     {
    11         Test test1 = new Test("one");  // 创建名为one的线程
    12         Test test2 = new Test("two");  // 创建名为two的线程
    13         
    14         test1.start();  // one线程执行
    15         test2.start();  // two线程执行
    16         
    17         for(int i=0;i<20;i++)
    18         {
    19             System.out.println("main run..." + i);  // 主线程执行
    20         }
    21     }
    22 }
    23 
    24 class Test extends Thread
    25 {
    26     private String name;
    27     
    28     public Test(String name)
    29     {
    30         this.name = name;
    31     }
    32     
    33     @Override
    34     public void run()
    35     {
    36         for(int i=0;i<20;i++)
    37         {
    38             System.out.println(name + " run..." + i);
    39         }
    40     }
    41 }
     1 /**
     2  * 通过继承Thread类方法,完成下面程序,
     3  * 引出创建线程的第2种更好的方法。
     4  * 需求:创建四个线程,完成售票任务
     5  */
     6 package Thread;
     7 
     8 public class ThreadDemo3
     9 {
    10     public static void main(String[] args)
    11     {
    12         // 创建四个线程
    13         Ticket t1 = new Ticket();
    14         Ticket t2 = new Ticket();
    15         Ticket t3 = new Ticket();
    16         Ticket t4 = new Ticket();
    17         
    18         // 启动并运行四个线程
    19         t1.start();
    20         t2.start();
    21         t3.start();
    22         t4.start();
    23         
    24         /**
    25          * 结果显示:
    26          * 四个线程都卖了100张票,相当于总共有400张票,而不是100张;
    27          * 原因:根据目前代码来说,创建一个对象(线程)就拥有100张票,所以应该
    28          * 让四个线程共同拥有100张票,使资源被独立共享,将代码进行优化
    29          * 
    30          * 通过优化之后代码,可知第一种创建线程的方法不太满足需求,改成第2种方式
    31          */
    32         
    33         /**
    34          * 错误的优化后的代码:只创建一个线程,让它运行四次
    35          * 结果抛出异常:IllegalThreadStateException
    36          * 无效线程状态异常,因为线程已经被启动了,又重新启动了三次
    37          */
    38         /*Ticket t1 = new Ticket();
    39         t1.start();
    40         t1.start();
    41         t1.start();
    42         t1.start();*/
    43         
    44     }
    45 }
    46 
    47 class Ticket extends Thread
    48 {
    49     // 优化之前的 ticket 属性
    50     // private int ticket = 100;
    51     
    52     // 优化之后的 ticket 属性
    53     // 但我们一般不定义为 static,声明周期太长
    54     private static int ticket = 100;
    55     
    56     @Override
    57     public void run()
    58     {
    59         while(true)
    60         {
    61             if(ticket > 0)
    62             {
    63                 System.out.println(currentThread().getName() + " sale:" + ticket--);
    64             }
    65         }
    66     }
    67 }
     1 /**
     2  * 创建线程的第2种方法:实现Runnable接口
     3  * 优化卖票程序
     4  */
     5 package Thread;
     6 
     7 public class ThreadDemo4
     8 {
     9     public static void main(String[] args)
    10     {
    11         Tickets t = new Tickets();
    12         
    13         Thread t1 = new Thread(t);
    14         Thread t2 = new Thread(t);
    15         Thread t3 = new Thread(t);
    16         Thread t4 = new Thread(t);
    17         
    18         t1.start();
    19         t2.start();
    20         t3.start();
    21         t4.start();
    22     }
    23 }
    24 
    25 class Tickets implements Runnable
    26 {
    27     private int ticket = 100;
    28     
    29     @Override
    30     public void run()
    31     {
    32         while(true)
    33         {
    34             if(ticket > 0)
    35             {
    36                 System.out.println(Thread.currentThread().getName() + " sale:" + ticket--);
    37             }
    38         }
    39     }
    40 }
     1 /**
     2  * 模拟多线程的安全问题,引出解决方法
     3  * 一般都是通过Thread类的sleep()方法来模拟多线程改的安全问题
     4  */
     5 package Thread;
     6 
     7 public class ThreadDemo5
     8 {
     9     public static void main(String[] args)
    10     {
    11         Ticket2 t = new Ticket2();
    12         
    13         Thread t1 = new Thread(t);
    14         Thread t2 = new Thread(t);
    15         Thread t3 = new Thread(t);
    16         Thread t4 = new Thread(t);
    17         
    18         t1.start();
    19         t2.start();
    20         t3.start();
    21         t4.start();
    22     }
    23 }
    24 
    25 class Ticket2 implements Runnable
    26 {
    27     private int ticket = 100;
    28     
    29     @Override
    30     public void run() 
    31     {
    32         while(true)
    33         {
    34             if(ticket > 0)
    35             {
    36                 try
    37                 {
    38                     // 只能被try-catch,不能抛出异常,因为 Runnable接口的run()方法没有抛出这个异常
    39                     // 加了这句话之后,多线程共享的数据ticket可能最后就会出现负数
    40                     Thread.sleep(10);
    41                 } catch (InterruptedException e)
    42                 {
    43                     e.printStackTrace();
    44                 }
    45                 System.out.println(Thread.currentThread().getName() + " sale:" + ticket--);
    46             }
    47         }
    48     }
    49     
    50 }
     1 /**
     2  * 同步代码块
     3  */
     4 package Thread;
     5 
     6 public class ThreadDemo6
     7 {
     8     public static void main(String[] args)
     9     {
    10         Ticket3 t = new Ticket3();
    11         Thread t1 = new Thread(t);
    12         Thread t2 = new Thread(t);
    13         Thread t3 = new Thread(t);
    14         Thread t4 = new Thread(t);
    15         
    16         t1.start();
    17         t2.start();
    18         t3.start();
    19         t4.start();
    20     }
    21 }
    22 
    23 class Ticket3 implements Runnable
    24 {
    25     private int ticket = 100;
    26     
    27     Object obj = new Object();
    28     
    29     @Override
    30     public void run()
    31     {
    32         while(true)
    33         {
    34             // 目前来看,随便放一个对象都可以,但必须是对象
    35             synchronized (obj)
    36             {
    37                 if(ticket > 0)
    38                 {
    39                     try
    40                     {
    41                         Thread.sleep(100);
    42                     } catch (InterruptedException e)
    43                     {
    44                         e.printStackTrace();
    45                     }
    46                     System.out.println(Thread.currentThread().getName() + " sale:" + ticket--);
    47                 }
    48             }
    49             
    50         }
    51     }
    52     
    53 }
     1 /**
     2  * 如何查看多线程代码是否有安全问题:
     3  * 1.明确哪些代码是多线程运行代码
     4  * 2.明确共享数据
     5  * 3.明确多线程运行代码中哪些语句是操作共享数据的,
     6  * 如果这些操作共享数据的代码不能被一个线程全部执行完再由其它线程执行,就会出现安全问题
     7  * 
     8  * 需求描述:
     9  * 两个顾客存钱,两人都存300,但每次只存100,存3次
    10  */
    11 package Thread;
    12 
    13 public class ThreadDemo7
    14 {
    15     public static void main(String[] args)
    16     {
    17         Cus c = new Cus();
    18         
    19         Thread t1 = new Thread(c);
    20         Thread t2 = new Thread(c);
    21         
    22         t1.start();
    23         t2.start();
    24     }
    25 }
    26 
    27 class Bank
    28 {
    29     private int sum;
    30     Object obj = new Object();
    31     
    32     public void add(int n)
    33     {
    34         /**
    35          * sum是共享数据,下面两句都是操作共享数据的,可能出现线程安全问题,
    36          * 中间加入sleep()来模拟线程安全问题,结果显示确实有安全问题存在,
    37          * 所以这两条语句要加上锁
    38          */
    39         synchronized (obj)
    40         {
    41             sum += n;
    42             try
    43             {
    44                 Thread.sleep(10);
    45             } catch (InterruptedException e)
    46             {
    47                 e.printStackTrace();
    48             }
    49             System.out.println("sum:" + sum);
    50         }
    51     }
    52 }
    53 
    54 class Cus implements Runnable
    55 {
    56     Bank b = new Bank();
    57     
    58     @Override
    59     public void run()
    60     {
    61         for(int i=0;i<3;i++)
    62         {
    63             b.add(100);
    64         }
    65     }
    66 }
     1 /**
     2  * 通过同步函数的方法优化上面的示例
     3  */
     4 package Thread;
     5 
     6 public class ThreadDemo8
     7 {
     8     public static void main(String[] args)
     9     {
    10         Cus2 c = new Cus2();
    11         
    12         Thread t1 = new Thread(c);
    13         Thread t2 = new Thread(c);
    14         
    15         t1.start();
    16         t2.start();
    17     }
    18 }
    19 
    20 class Bank2
    21 {
    22     private int sum;
    23     
    24     // 因为方法里面只有两条语句,且这两条语句就是操作共享数据的
    25     // 所以这个方法可以直接实现同步
    26     public synchronized void add(int n)
    27     {
    28         sum += n;
    29         try
    30         {
    31             Thread.sleep(10);
    32         } catch (InterruptedException e)
    33         {
    34             e.printStackTrace();
    35         }
    36         System.out.println("sum:" + sum);
    37     }
    38 }
    39 
    40 class Cus2 implements Runnable
    41 {
    42     Bank2 b = new Bank2();
    43     
    44     @Override
    45     public void run()
    46     {
    47         for(int i=0;i<3;i++)
    48         {
    49             b.add(100);
    50         }
    51     }
    52 }
     1 /**
     2  * 使用同步函数的方法来优化卖票的例子
     3  */
     4 package Thread;
     5 
     6 public class ThreadDemo9
     7 {
     8     public static void main(String[] args)
     9     {
    10         Ticket4 t = new Ticket4();
    11         
    12         Thread t1 = new Thread(t);
    13         Thread t2 = new Thread(t);
    14         Thread t3 = new Thread(t);
    15         Thread t4 = new Thread(t);
    16         
    17         t1.start();
    18         t2.start();
    19         t3.start();
    20         t4.start();
    21     }
    22 }
    23 
    24 class Ticket4 implements Runnable
    25 {
    26     private int ticket = 100;
    27     
    28     // 不能直接在run()方法上进行同步,因为里面有非共享资源被操作
    29     // 一定要确保只同步操纵共享资源的语句
    30     @Override
    31     public void run() 
    32     {
    33         while(true)
    34         {
    35             synch();
    36             
    37             /*
    38              * 这段被共享资源被操作的代码放到一个新函数中
    39              * if(ticket > 0)
    40             {
    41                 try
    42                 {
    43                     Thread.sleep(10);
    44                 } catch (InterruptedException e)
    45                 {
    46                     e.printStackTrace();
    47                 }
    48                 System.out.println(Thread.currentThread().getName() + " sale:" + ticket--);
    49             }*/
    50         }
    51     }
    52     
    53     // 重新定义一个方法,将只操作共享资源的语句包含进来
    54     public synchronized void synch()
    55     {
    56         if(ticket > 0)
    57         {
    58             try
    59             {
    60                 Thread.sleep(10);
    61             } catch (InterruptedException e)
    62             {
    63                 e.printStackTrace();
    64             }
    65             System.out.println(Thread.currentThread().getName() + " sale:" + ticket--);
    66         }
    67     }
    68 }
     1 /**
     2  * 同步函数用的是哪个锁?
     3  * 函数需要被对象调用,那么函数都有一个所属对象引用,就是this
     4  * 那么同步函数用的锁就是this
     5  * 
     6  * 验证:
     7  * 两个线程实现卖票任务:
     8  * 一个线程使用同步代码块
     9  * 一个线程使用同步函数
    10  */
    11 package Thread;
    12 
    13 public class ThreadDemo10
    14 {
    15     public static void main(String[] args)
    16     {
    17         Ticket5 t = new Ticket5();
    18         
    19         Thread t1 = new Thread(t);
    20         Thread t2 = new Thread(t);
    21         
    22         t1.start();
    23         try
    24         {
    25             // 让主线程停一会,不然可能一下子让两个线程执行的都是synch()方法或代码块内容,达不到印证问题的效果
    26             // 不便于问题的出现
    27             Thread.sleep(10);
    28         } catch (InterruptedException e)
    29         {
    30             e.printStackTrace();
    31         }
    32         t.flag = false;
    33         t2.start();
    34     }
    35 }
    36 
    37 class Ticket5 implements Runnable
    38 {
    39     private int ticket = 100;
    40     boolean flag = true;
    41     Object obj = new Object();
    42     
    43     @Override
    44     public void run() 
    45     {
    46         if(flag)
    47         {
    48             while(true)
    49             {
    50                 // 用的锁是Object对象
    51                 // 两个线程用的不是同一个锁,安全问题还是会出现,两个前提没有满足
    52                 // 把obj改成this,即可解决问题,说明同步函数用的锁就是this
    53                 synchronized (obj)
    54                 {
    55                     if(ticket > 0)
    56                     {
    57                         try
    58                         {
    59                             Thread.sleep(10);
    60                         } catch (InterruptedException e)
    61                         {
    62                             e.printStackTrace();
    63                         }
    64                         System.out.println(Thread.currentThread().getName() + " code:" + ticket--);
    65                     }
    66                 }
    67                 
    68             }
    69         }
    70         else
    71         {
    72             while(true)
    73             {
    74                 // 用的是锁是this
    75                 this.synch();
    76             }
    77         }
    78         
    79     }
    80     
    81     public synchronized void synch()
    82     {
    83         if(ticket > 0)
    84         {
    85             try
    86             {
    87                 Thread.sleep(10);
    88             } catch (InterruptedException e)
    89             {
    90                 e.printStackTrace();
    91             }
    92             System.out.println(Thread.currentThread().getName() + " synch():" + ticket--);
    93         }
    94     }
    95     
    96 }
     1 /**
     2  * 静态方法使用的锁是哪个?
     3  * 不是this,静态方法中不可以定义this
     4  * 静态进内存时,没有本类对象,但有本类对应的字节码文件对象
     5  * 类名.class 该对象的类型为Class
     6  * 所以:静态方法使用的锁是该方法所在类的字节码文件对象
     7  */
     8 package Thread;
     9 
    10 public class ThreadDemo11
    11 {
    12     public static void main(String[] args)
    13     {
    14         Ticket6 t = new Ticket6();
    15         
    16         Thread t1 = new Thread(t);
    17         Thread t2 = new Thread(t);
    18         
    19         t1.start();
    20         try
    21         {
    22             Thread.sleep(10);
    23         } catch (InterruptedException e)
    24         {
    25             e.printStackTrace();
    26         }
    27         t.flag = false;
    28         t2.start();
    29     }
    30 }
    31 
    32 class Ticket6 implements Runnable
    33 {
    34     private static int ticket = 100;
    35     boolean flag = true;
    36     Object obj = new Object();
    37     
    38     @Override
    39     public void run() 
    40     {
    41         if(flag)
    42         {
    43             while(true)
    44             {
    45                 // 此时使用this,还是会出现安全问题
    46                 // 将this改成Ticket6.class,安全问题被解决
    47                 // 说明此时使用的锁是本类对应的字节码文件对象
    48                 synchronized (this)
    49                 {
    50                     if(ticket > 0)
    51                     {
    52                         try
    53                         {
    54                             Thread.sleep(10);
    55                         } catch (InterruptedException e)
    56                         {
    57                             e.printStackTrace();
    58                         }
    59                         System.out.println(Thread.currentThread().getName() + " code:" + ticket--);
    60                     }
    61                 }
    62                 
    63             }
    64         }
    65         else
    66         {
    67             while(true)
    68             {
    69                 synch();
    70             }
    71         }
    72         
    73     }
    74     
    75     public static synchronized void synch()
    76     {
    77         if(ticket > 0)
    78         {
    79             try
    80             {
    81                 Thread.sleep(10);
    82             } catch (InterruptedException e)
    83             {
    84                 e.printStackTrace();
    85             }
    86             System.out.println(Thread.currentThread().getName() + " synch():" + ticket--);
    87         }
    88     }
    89 }
     1 /**
     2  * 写个程序,引出死锁问题
     3  * 一般出现死锁都是因为同步中嵌套着同步,如下面程序:
     4  * 同步代码块中嵌套着同步函数
     5  * 同步函数中嵌套着同步代码块
     6  */
     7 package Thread;
     8 
     9 public class ThreadDemo12
    10 {
    11     public static void main(String[] args)
    12     {
    13         Ticket7 t = new Ticket7();
    14         Thread t1 = new Thread(t);
    15         Thread t2 = new Thread(t);
    16         
    17         t1.start();
    18         try
    19         {
    20             Thread.sleep(10);
    21         } catch (InterruptedException e)
    22         {
    23             e.printStackTrace();
    24         }
    25         t.flag = false;
    26         t2.start();
    27     }
    28 }
    29 
    30 class Ticket7 implements Runnable
    31 {
    32     private int ticket = 100;
    33     boolean flag = true;
    34     Object obj = new Object();
    35     
    36     @Override
    37     public void run()
    38     {
    39         if(flag)
    40         {
    41             while(true)
    42             {
    43                 synchronized(obj)
    44                 {
    45                     this.synch();
    46                 }
    47             }
    48         }
    49         else
    50         {
    51             while(true)
    52             {
    53                 this.synch();
    54             }
    55         }
    56     }
    57     
    58     public synchronized void synch()
    59     {
    60         synchronized(obj)
    61         {
    62             if(ticket > 0)
    63             {
    64                 try
    65                 {
    66                     Thread.sleep(10);
    67                 } catch (InterruptedException e)
    68                 {
    69                     e.printStackTrace();
    70                 }
    71                 System.out.println(Thread.currentThread().getName() + " code:" + ticket--);
    72             }
    73             
    74         }
    75     }
    76 }
     1 /**
     2  * 一个简单的死锁程序
     3  * 
     4  * 拿到一个运行结果:
     5  * lf locka
     6  * else lockb
     7  */
     8 package Thread;
     9 
    10 public class ThreadDemo13
    11 {
    12     public static void main(String[] args)
    13     {
    14         Thread t1 = new Thread(new DeadLock(true));
    15         Thread t2 = new Thread(new DeadLock(false));
    16         
    17         t1.start();
    18         t2.start();
    19     }
    20 }
    21 
    22 class DeadLock implements Runnable
    23 {
    24     boolean flag;
    25     public DeadLock(boolean flag)
    26     {
    27         this.flag = flag;
    28     }
    29 
    30     @Override
    31     public void run()
    32     {
    33         if(flag)
    34         {
    35             // 加while循环,是为了多运行几次,那样就肯定会出现死锁问题
    36             // 有时候一次两次可能不会出现
    37             while(true)
    38             {
    39                 // 根据结果得出:
    40                 // 一个线程拿到locka锁,但lockb锁进不去
    41                 // 另一个线程拿到lockb锁,但locka锁进不去
    42                 // 两者都不肯放弃锁,就变成了死锁
    43                 synchronized (MyLock.locka)
    44                 {
    45                     System.out.println("lf locka");
    46                     synchronized (MyLock.lockb)
    47                     {
    48                         System.out.println("if lockb");
    49                     }
    50                 }
    51             }
    52         }
    53         else
    54         {
    55             while(true)
    56             {
    57                 synchronized (MyLock.lockb)
    58                 {
    59                     System.out.println("else lockb");
    60                     synchronized (MyLock.locka)
    61                     {
    62                         System.out.println("else locka");
    63                     }
    64                 }
    65             }
    66         }
    67     }
    68     
    69 }
    70 
    71 class MyLock
    72 {
    73     // 定义两个锁,目前来看,锁就是对象
    74     static Object locka = new Object();
    75     static Object lockb = new Object();
    76 }
      1 /**
      2  * 线程间通信:wait(),notify(),notifyAll()方法的使用
      3  * 
      4  * 需求:
      5  * 一个共享资源
      6  * 两个线程
      7  * 一个线程往资源里面存内容
      8  * 一个线程往资源里面取内容
      9  * 
     10  * 同步的两个前提
     11  * 
     12  * 等待唤醒机制:实现存一次,取一次
     13  * 等待和唤醒必须是同一个锁
     14  */
     15 package Thread;
     16 
     17 public class ThreadDemo14
     18 {
     19     public static void main(String[] args)
     20     {
     21         // 一个共享资源
     22         Res r = new Res();
     23         
     24         // 两个线程共同操作这个资源
     25         Input in = new Input(r);
     26         Output out = new Output(r);
     27         
     28         Thread t1 = new Thread(in);
     29         Thread t2 = new Thread(out);
     30         
     31         t1.start();
     32         t2.start();
     33         
     34     }
     35 }
     36 
     37 class Res 
     38 {
     39     String name;
     40     String gender;
     41     boolean flag = false;
     42 }
     43 
     44 class Input implements Runnable
     45 {
     46     private Res r;
     47     public Input(Res r)
     48     {
     49         this.r = r;
     50     }
     51     
     52     @Override
     53     public void run()
     54     {
     55         int x = 0;
     56         
     57         while(true)
     58         {
     59             // 对共享资源加锁,实现同步的第1个前提(两个地方都加锁,才能说明是两个线程操作共享资源)
     60             // 两个线程用的锁必须是一样的才行,这里用this不行,要体现出唯一性
     61             // r,Input.class, Output.class三个锁都可以,因为都是唯一的
     62             synchronized (r)
     63             {
     64                 if(r.flag)
     65                 {
     66                     try
     67                     {
     68                         r.wait();  // 睡眠的线程存放在内存的线程池里面
     69                     } catch (InterruptedException e)
     70                     {
     71                         e.printStackTrace();
     72                     }
     73                 }
     74                 
     75                 if(x == 0)
     76                 {
     77                     r.name = "zhangsan";
     78                     r.gender = "nan";
     79                 }
     80                 else
     81                 {
     82                     r.name = "lisi";
     83                     r.gender = "nv";
     84                 }
     85                 
     86                 x = (x+1)%2;
     87                 
     88                 r.flag = true;
     89                 
     90                 r.notify();  // 叫醒线程池里面的线程
     91             }
     92             
     93         }
     94     }
     95 }
     96 
     97 class Output implements Runnable
     98 {
     99     Res r;
    100     public Output(Res r)
    101     {
    102         this.r = r;
    103     }
    104     
    105     @Override
    106     public void run()
    107     {
    108         while(true)
    109         {
    110             // 对共享资源加锁,实现同步的第1个前提(两个地方都加锁,才能说明是两个线程操作共享资源)
    111             synchronized (r)
    112             {
    113                 if(! r.flag)
    114                 {
    115                     try
    116                     {
    117                         r.wait();
    118                     } catch (InterruptedException e)
    119                     {
    120                         e.printStackTrace();
    121                     }
    122                 }
    123                 
    124                 System.out.println("name:" + r.name + ";gender:" + r.gender);
    125                 
    126                 r.flag = false;
    127                 
    128                 r.notify();
    129             }
    130         }
    131     }
    132 }
      1 /**
      2  * 线程间通信,对上面例子进行优化
      3  */
      4 package Thread;
      5 
      6 public class ThreadDemo14
      7 {
      8     public static void main(String[] args)
      9     {
     10         Res r = new Res();
     11         
     12         Input in = new Input(r);
     13         Output out = new Output(r);
     14         
     15         Thread t1 = new Thread(in);
     16         Thread t2 = new Thread(out);
     17         
     18         t1.start();
     19         t2.start();
     20     }
     21 }
     22 
     23 class Res 
     24 {
     25     private String name;
     26     private String gender;
     27     private boolean flag = false;
     28     
     29     public synchronized void set(String name, String gender)
     30     {
     31         if(flag)
     32         {
     33             try
     34             {
     35                 this.wait();
     36             } catch (InterruptedException e)
     37             {
     38                 e.printStackTrace();
     39             }
     40         }
     41         
     42         this.name = name;
     43         this.gender = gender;
     44         
     45         flag = true;
     46         
     47         this.notify();
     48     }
     49     
     50     public synchronized void syso()
     51     {
     52         if(!flag)
     53         {
     54             try
     55             {
     56                 this.wait();
     57             } catch (InterruptedException e)
     58             {
     59                 e.printStackTrace();
     60             }
     61         }
     62         
     63         System.out.println("name:" + name + ";gender:" + gender);
     64         
     65         flag = false;
     66         
     67         this.notify();
     68         
     69     }
     70 }
     71 
     72 class Input implements Runnable
     73 {
     74     private Res r;
     75     public Input(Res r)
     76     {
     77         this.r = r;
     78     }
     79     
     80     @Override
     81     public void run()
     82     {
     83         int x = 0;
     84         
     85         while(true)
     86         {
     87             synchronized (r)
     88             {
     89                 if(x == 0)
     90                 {
     91                     r.set("zhangsan", "nan");
     92                 }
     93                 else
     94                 {
     95                     r.set("lisi", "nv");
     96                 }
     97                 
     98                 x = (x+1)%2;
     99             }
    100             
    101         }
    102     }
    103 }
    104 
    105 class Output implements Runnable
    106 {
    107     Res r;
    108     public Output(Res r)
    109     {
    110         this.r = r;
    111     }
    112     
    113     @Override
    114     public void run()
    115     {
    116         while(true)
    117         {
    118             synchronized (r)
    119             {
    120                 r.syso();
    121             }
    122         }
    123     }
    124 }
      1 /**
      2  * 优化上个例子代码,上面的例子只适合有一个存线程,一个取线程
      3  * 
      4  * 生产者-消费者,添加标记
      5  * 
      6  * 解决实际出现的两个问题,在不知两个线程的情况下:
      7  * 生产两个但只消费一个产品,或值生产一个但消费两个产品(while循环判断标记解决)
      8  * 死锁问题(通知所有等待线程解决)
      9  */
     10 package Thread;
     11 
     12 public class ThreadDemo15
     13 {
     14     public static void main(String[] args)
     15     {
     16         Resource res = new Resource();
     17         
     18         Producer pro = new Producer(res);
     19         Consumer con = new Consumer(res);
     20         
     21         // 两个生产者
     22         Thread t1 = new Thread(pro);
     23         Thread t2 = new Thread(pro);
     24         // 两个消费者
     25         Thread t3 = new Thread(con);
     26         Thread t4 = new Thread(con);
     27         
     28         t1.start();
     29         t2.start();
     30         t3.start();
     31         t4.start();
     32     }
     33 }
     34 
     35 class Resource
     36 {
     37     private String name;
     38     private int count = 0;
     39     boolean flag = false;
     40     
     41     public synchronized void set(String name)
     42     {
     43         // 将if改成while,否则会出现生产两个消费一个或生产一个消费两个的问题
     44         while(flag)
     45         {
     46             try
     47             {
     48                 this.wait();
     49             } catch (InterruptedException e)
     50             {
     51                 e.printStackTrace();
     52             }
     53         }
     54         
     55         this.name = name + (count++);
     56         System.out.println(Thread.currentThread().getName() + "生产者.." + this.name);
     57         
     58         flag = true;
     59         
     60         // 将notify()改成notifyAll(),解决死锁问题
     61         this.notifyAll();
     62     }
     63     
     64     public synchronized void syso()
     65     {
     66         while(!flag)
     67         {
     68             try
     69             {
     70                 this.wait();
     71             } catch (InterruptedException e)
     72             {
     73                 e.printStackTrace();
     74             }
     75         }
     76         
     77         System.out.println(Thread.currentThread().getName() + "消费者...." + this.name);
     78         
     79         flag = false;
     80         
     81         this.notifyAll();
     82     }
     83     
     84 }
     85 
     86 class Producer implements Runnable
     87 {
     88     private Resource res;
     89     public Producer(Resource res)
     90     {
     91         this.res = res;
     92     }
     93     
     94     @Override
     95     public void run()
     96     {
     97         while(true)
     98         {
     99             res.set("商品");
    100         }
    101     }
    102     
    103 }
    104 
    105 class Consumer implements Runnable
    106 {
    107     private Resource res;
    108     public Consumer(Resource res)
    109     {
    110         this.res = res;
    111     }
    112     
    113     @Override
    114     public void run()
    115     {
    116         while(true)
    117         {
    118             res.syso();
    119         }
    120     }
    121 }
      1 /**
      2  * 优化上个例子代码
      3  * 
      4  * JDK1.5之后,synchronized、wait、notify、notifyAll都不用了
      5  * 有新的接口、类和方法来替代
      6  * 并实现的可以在本方只唤醒对象的操作
      7  * 
      8  * 之前的例子是一下子唤醒所有线程,应该只唤醒对方的线程
      9  * 
     10  * 之前一个锁,只能对应一组wait,notify
     11  * 现在一个锁,可以对应多组wait,notify
     12  */
     13 package Thread;
     14 
     15 import java.util.concurrent.locks.Condition;
     16 import java.util.concurrent.locks.Lock;
     17 import java.util.concurrent.locks.ReentrantLock;
     18 
     19 public class ThreadDemo15
     20 {
     21     public static void main(String[] args)
     22     {
     23         Resource res = new Resource();
     24         
     25         Producer pro = new Producer(res);
     26         Consumer con = new Consumer(res);
     27         
     28         // 两个生产者
     29         Thread t1 = new Thread(pro);
     30         Thread t2 = new Thread(pro);
     31         // 两个消费者
     32         Thread t3 = new Thread(con);
     33         Thread t4 = new Thread(con);
     34         
     35         t1.start();
     36         t2.start();
     37         t3.start();
     38         t4.start();
     39     }
     40 }
     41 
     42 class Resource
     43 {
     44     private String name;
     45     private int count = 0;
     46     boolean flag = false;
     47     // 产生锁
     48     private Lock lock = new ReentrantLock();
     49     
     50     // 通过锁获得监视器方法(wait,notify,notifyAll)所在类对象
     51     // 定义生产者这边的包含监视器方法的对象
     52     private Condition condition_pro = lock.newCondition();
     53     // 定义消费者这边的包含监视器方法的对象
     54     private Condition condition_con = lock.newCondition();
     55     
     56     public void set(String name) throws InterruptedException
     57     {
     58         // 加锁
     59         lock.lock();
     60         
     61         try
     62         {
     63             while(flag)
     64             {
     65                 // 等待
     66                 // 生产者已生产了商品,不能再次生产,必须等待消费者消费
     67                 condition_pro.await();
     68             }
     69             
     70             this.name = name + (count++);
     71             System.out.println(Thread.currentThread().getName() + "生产者.." + this.name);
     72             
     73             flag = true;
     74             
     75             // 唤醒
     76             // 通知消费者消费商品
     77             condition_con.signal();
     78             
     79         }
     80         finally
     81         {
     82             // 释放锁
     83             lock.unlock();  // 释放锁的动作一定要执行
     84         }
     85         
     86     }
     87     
     88     public void syso() throws InterruptedException
     89     {
     90         lock.lock();
     91         
     92         try
     93         {
     94             while(!flag)
     95             {
     96                 // 消费者已消费商品,不能再消费,必须等待生产者生产
     97                 condition_con.await();
     98             }
     99             
    100             System.out.println(Thread.currentThread().getName() + "消费者...." + this.name);
    101             
    102             flag = false;
    103             
    104             // 通知生产者生产商品
    105             condition_pro.signalAll();
    106         }
    107         finally
    108         {
    109             lock.unlock();
    110         }
    111     }
    112 }
    113 
    114 class Producer implements Runnable
    115 {
    116     private Resource res;
    117     public Producer(Resource res)
    118     {
    119         this.res = res;
    120     }
    121     
    122     @Override
    123     public void run()
    124     {
    125         while(true)
    126         {
    127             try
    128             {
    129                 res.set("商品");
    130             } catch (InterruptedException e)
    131             {
    132                 e.printStackTrace();
    133             }
    134         }
    135     }
    136     
    137 }
    138 
    139 class Consumer implements Runnable
    140 {
    141     private Resource res;
    142     public Consumer(Resource res)
    143     {
    144         this.res = res;
    145     }
    146     
    147     @Override
    148     public void run()
    149     {
    150         while(true)
    151         {
    152             try
    153             {
    154                 res.syso();
    155             } catch (InterruptedException e)
    156             {
    157                  e.printStackTrace();
    158             }
    159         }
    160     }
    161 }
     1 /**
     2  * 实现停止线程
     3  * 
     4  * stop()方法已过时,如何停止线程:
     5  * 只有一种方法,就是是run()方法结束
     6  * 原理:
     7  *  开启多线程运行,运行代码通常是循环结构,
     8  *  只要控制住循环,就可以让run方法结束,也就是让线程结束
     9  *  
    10  */
    11 package Thread;
    12 
    13 public class ThreadDemo16
    14 {
    15     public static void main(String[] args)
    16     {
    17         Inter inter = new Inter();
    18         
    19         Thread t1 = new Thread(inter);
    20         Thread t2 = new Thread(inter);
    21         
    22         t1.start();
    23         t2.start();
    24         
    25         int count = 0;
    26         
    27         while(true)
    28         {
    29             // 实现计数到60之后让线程结束
    30             if(count++ == 60)
    31             {
    32                 inter.change();
    33                 break;
    34             }
    35             System.out.println(Thread.currentThread().getName() + "...main...");
    36         }
    37         System.out.println(Thread.currentThread().getName() + "...over...");
    38     }
    39 }
    40 
    41 class Inter implements Runnable
    42 {
    43     private boolean flag = true;
    44 
    45     @Override
    46     public void run()
    47     {
    48         while(flag)
    49         {
    50             System.out.println(Thread.currentThread().getName() + "...run...");
    51         }
    52     }
    53     
    54     // 在指定的条件下调用此方法,即可停止循环,结束线程
    55     public void change()
    56     {
    57         flag = false;
    58     }
    59 }
     1 /**
     2  * 实现停止线程:interrupt()方法的使用
     3  * 
     4  * stop()方法已过时,如何停止线程:
     5  * 只有一种方法,就是是run()方法结束
     6  * 原理:
     7  *  开启多线程运行,运行代码通常是循环结构,
     8  *  只要控制住循环,就可以让run方法结束,也就是让线程结束
     9  *  
    10  *  特殊情况:
    11  *  当线程处于冻结状态,就不会读取到标记,那么线程就不会结束
    12  *  
    13  *  当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,
    14  *  使用interrupt()方法来唤醒睡眠的 线程,但会抛出异常
    15  *  强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束
    16  */
    17 package Thread;
    18 
    19 public class ThreadDemo16
    20 {
    21     public static void main(String[] args)
    22     {
    23         Inter inter = new Inter();
    24         
    25         Thread t1 = new Thread(inter);
    26         Thread t2 = new Thread(inter);
    27         
    28         t1.start();
    29         t2.start();
    30         
    31         int count = 0;
    32         
    33         while(true)
    34         {
    35             // 实现计数到60之后让通过interrupt()唤醒线程
    36             if(count++ == 60)
    37             {
    38                 // 唤醒两个线程
    39                 t1.interrupt();
    40                 t2.interrupt();
    41                 break;
    42             }
    43             System.out.println(Thread.currentThread().getName() + "...main...");
    44         }
    45         System.out.println(Thread.currentThread().getName() + "...over...");
    46     }
    47 }
    48 
    49 class Inter implements Runnable
    50 {
    51     private boolean flag = true;
    52 
    53     // 这样写,线程1和线程2都会冻结
    54     @Override
    55     public synchronized void run()
    56     {
    57         while(flag)
    58         {
    59             try
    60             {
    61                 wait();
    62             } catch (InterruptedException e)
    63             {
    64                 // 进入异常,说明就有interrupt()强制唤醒线程,
    65                 // 接着另 flag = false; 使可以运行的线程正常结束
    66                 flag = false;
    67                 System.out.println(Thread.currentThread().getName() + "...exception...");
    68             }
    69             System.out.println(Thread.currentThread().getName() + "...run...");
    70         }
    71     }
    72     
    73     // 在指定的条件下调用此方法,即可停止循环,结束线程
    74     public void change()
    75     {
    76         flag = false;
    77     }
    78 }
     1 /**
     2  * 守护线程
     3  * 
     4  * 守护线程可以理解为后台线程,其他线程可以理解为前台线程
     5  * 
     6  * 区别:
     7  * 当守护线程和其它线程一起执行任务时,没有区别,共同抢夺cpu时间片;
     8  * 但到其他线程结束时,只要守护线程不是处于结束状态,守护线程都会自动结束
     9  */
    10 package Thread;
    11 
    12 public class ThreadDemo16
    13 {
    14     public static void main(String[] args)
    15     {
    16         Inter inter = new Inter();
    17         
    18         Thread t1 = new Thread(inter);
    19         Thread t2 = new Thread(inter);
    20         
    21         // 调用此方法是t1和t2两个线程都变为守护线程
    22         // 不设为守护线程之前,这段代码最后的结果是t1,t2两个线程一直
    23         // 处于运行状态,此程序永远不会结束
    24         // 设为守护线程后,当主线程结束后,两个正在执行的线程也会跟着结束
    25         t1.setDaemon(true);
    26         t2.setDaemon(true);
    27         t1.start();
    28         t2.start();
    29         
    30         int count = 0;
    31         
    32         while(true)
    33         {
    34             if(count++ == 60)
    35             {
    36                 break;
    37             }
    38             System.out.println(Thread.currentThread().getName() + "...main...");
    39         }
    40         System.out.println(Thread.currentThread().getName() + "...over...");
    41     }
    42 }
    43 
    44 class Inter implements Runnable
    45 {
    46     private boolean flag = true;
    47 
    48     // 这样写,线程1和线程2都会冻结
    49     @Override
    50     public void run()
    51     {
    52         while(flag)
    53         {
    54             System.out.println(Thread.currentThread().getName() + "...run...");
    55         }
    56     }
    57 }
     1 /**
     2  * join()方法的特性
     3  * 
     4  * 被用来临时加入线程运行
     5  */
     6 package Thread;
     7 
     8 public class ThreadDemo17
     9 {
    10     public static void main(String[] args)
    11     {
    12         JoinTest join = new JoinTest();
    13         
    14         Thread t1 = new Thread(join);
    15         Thread t2 = new Thread(join);
    16         
    17         t1.start();
    18         
    19         try
    20         {
    21             // 执行这条语句之后,主线程必须让出cpu执行权,
    22             // 让t1拿到执行权,把任务执行完毕之后才让出cpu执行权
    23             // 让主线程和t2开始抢cpu
    24             t1.join();
    25         } catch (InterruptedException e)
    26         {
    27             e.printStackTrace();
    28         }
    29         
    30         t2.start();
    31         
    32         
    33         /*try
    34         {
    35             // 如果这句话写在这里,那么主线程还是必须先让出执行权,
    36             // 让t1和t2争夺cpu执行权还行任务
    37             // 当t1线程执行结束之后,主线程就和t2开始抢cpu了
    38             t1.join();
    39         } catch (InterruptedException e)
    40         {
    41             e.printStackTrace();
    42         }*/
    43         
    44         for(int i=0;i<60;i++)
    45         {
    46             System.out.println(Thread.currentThread().getName() + "..main.." + i);
    47         }
    48         System.out.println(Thread.currentThread().getName() + "over");
    49     }
    50 }
    51 
    52 class JoinTest implements Runnable
    53 {
    54     @Override
    55     public void run()
    56     {
    57         for(int i=0;i<60;i++)
    58         {
    59             System.out.println(Thread.currentThread().getName() + "..run.." + i);
    60         }
    61     }
    62 }
  • 相关阅读:
    那些会阻碍程序员成长的细节[6]
    那些会阻碍程序员成长的细节[5]
    那些会阻碍程序员成长的细节[4]
    那些会阻碍程序员成长的细节[3]
    基于SpringCloud的Microservices架构实战案例-在线API管理
    主机地址变更后,dubbo请求时依旧会寻址旧IP的问题
    基于SpringCloud的Microservices架构实战案例-配置文件属性内容加解密
    基于SpringCloud的Microservices架构实战案例-架构拆解
    程序员创业必读的几本书
    Java基础---二分查找
  • 原文地址:https://www.cnblogs.com/kehuaihan/p/6777597.html
Copyright © 2020-2023  润新知