• 线程同步、死锁和通信——Java多线程(二)


    一、多线程同步

    上一篇随笔中,我曾遇到对多线程程序的多次运行结果不一致的情况,这主要是因为没有对这些线程在访问临界资源做必要的控制,而接下来就用线程的同步来解决这个问题。

    1.同步代码块

     1 class RunnableDemo implements Runnable 
     2 {
     3     private int tickets=5;
     4     public void run()
     5     {
     6         while(true)
     7         {
     8             synchronized(this)//同步代码块语法定义如下
     9             {
    10                 if(tickets<=0) break;
    11                 try{
    12                     Thread.sleep(100);
    13                 }
    14                 catch(Exception e){
    15                     e.printStackTrace();
    16                 }
    17                 System.out.println(Thread.currentThread().getName()+"出售票:"+tickets);
    18                 tickets -= 1;
    19             }
    20         }
    21     }
    22 }
    23 
    24 public class ThreadTest
    25 {
    26     public static void main(String[] args)
    27     {
    28         RunnableDemo r = new RunnableDemo();
    29         new Thread(r).start();
    30         new Thread(r).start();
    31         new Thread(r).start();
    32         new Thread(r).start();
    33     }
    34 }

    在同一时刻只能由一个线程进入同步代码块内运行,只有当该线程离开同步代码块后,其他线程才能进入同步代码块内运行。

    2.同步方法

    即:把上述例子中同步代码块的内容,专门封装在一个方法里,通过在run()方法中调用创建的方法实现相应的功能。

     1 class RunnableDemo implements Runnable 
     2 {
     3     private int tickets=5;
     4     public void run()
     5     {
     6         while(tickets>0)
     7         {
     8             sale();
     9         }
    10     }
    11     public synchronized void sale()//同步方法
    12     {
    13         if(tickets>0){
    14         try{
    15             Thread.sleep(100);
    16         }
    17         catch(Exception e){
    18             e.printStackTrace();
    19         }
    20         System.out.println(Thread.currentThread().getName()+"出售票:"+tickets);
    21         tickets -= 1;
    22         }
    23     }
    24 }
    25 
    26 public class ThreadTest
    27 {
    28     public static void main(String[] args)
    29     {
    30         RunnableDemo r = new RunnableDemo();
    31         new Thread(r).start();
    32         new Thread(r).start();
    33         new Thread(r).start();
    34         new Thread(r).start();
    35     }
    36 }

     二、死锁

    如果有一组进程(或线程),线程1 已经占据资源R1,并持有资源R1上的锁,而且正在等待资源R2开锁;线程2已经占据资源R2,并拥有资源R2上的锁,却正在等待R1开锁。那么这两个线程都不释放自己占据的资源,同时申请不到对方资源上的锁,它们只能永远等待下去。这种现象就叫做死锁。

    预防死锁的一种方法:利用有序资源分配策略——要求线程申请资源必须按照以编号上升的次序依次申请。

    三、线程间通信

    同属于一个进程的多个线程,是共享地址空间的,它们可以相互通信,共同协作来完成指定任务。

    Java是通过Object类的wait()notify()notifyAll()这几个方法来实现线程间的通信。

    wait():线程进入睡眠状态,直到其他线程进入并调用notify()notifyAll()为止。

    notify():唤醒在该同步代码块中第1个调用wait()的线程。

    notifyAll():唤醒在该同步代码块中所有调用wait()的线程,高优先级最先被唤醒。

     1 class Producer implements Runnable
     2 {
     3     Person q = null;
     4     public Producer(Person q)
     5     {
     6         this.q=q;
     7     }
     8     @Override
     9     public void run()
    10     {
    11         for(int i=0;i<10;i++)
    12         {
    13             if(i%2==0)
    14             {
    15                 q.set("张三","男");
    16             }
    17             else{
    18                 q.set("李四","女");
    19             }
    20         }
    21     }
    22 }
    23 class Consumer implements Runnable
    24 {
    25     Person q = null;
    26     public Consumer(Person q)
    27     {
    28         this.q=q;
    29     }
    30     @Override
    31     public void run() 
    32     {
    33         for(int i=0;i<10;++i)
    34         {
    35             q.get();
    36         }
    37     }
    38 }
    39 class Person
    40 {
    41     private String name = "李四";
    42     private String sex = "女";
    43     private boolean bFull = false;//当Consumer线程取走数据后,false
    44     public synchronized void set(String name,String sex)
    45     {
    46         if(bFull)
    47         {
    48             try
    49             {
    50                 wait();//后来的线程要等待
    51             }catch(InterruptedException e){
    52                 e.printStackTrace();
    53             }
    54         }
    55         this.name = name;
    56         this.sex = sex;
    57         bFull = true;//当Producer线程放入数据后,true
    58         notify();//唤醒最先到达的线程
    59     }
    60     public synchronized void get()
    61     {
    62         if(!bFull)
    63         {
    64             try{
    65                 wait();
    66             }catch(InterruptedException e){
    67                 e.printStackTrace();
    68             }
    69         }
    70         System.out.println(name+"——>"+sex);
    71         bFull = false;
    72         notify();
    73     }
    74 }
    75 public class ThreadCommunation
    76 {
    77     public static void main(String[] args)
    78     {
    79         Person q = new Person();
    80         new Thread(new Producer(q)).start();
    81         new Thread(new Consumer(q)).start();
    82     }
    83 }

    ***注意:wait()notify()notifyAll()这三个方法必须在synchronized方法中调用,该线程必须得到该对象的所有权。

    四、线程的生命周期

    控制线程生命周期的方法:suspend()resume()stop()方法,但是这三个方法都不推荐使用。

    若想控制线程的生命周期,推荐使用在run()方法中添加循环条件的方法来实现对线程生命周期的控制。

  • 相关阅读:
    class11_创建新的输出字段P2
    class10_创建新的输出字段
    class09_高级过滤数据
    Markdown高级语法
    class08_过滤数据
    class07_查询数据
    class06_插入数据
    class05_操纵表
    class04_创建表02
    class03_Create a new table by SQL
  • 原文地址:https://www.cnblogs.com/wxywxy/p/6786396.html
Copyright © 2020-2023  润新知