zoukankan      html  css  js  c++  java
  • 多线程---线程同步

    说到线程同步就不得不说电影院卖票的案例:

    • 某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票

    分析步骤:

    • 定义一个类SellTicket实现Runnable接口,里面定义一个成员变量:private int tickets = 100;

    • 在SellTicket类中重写run()方法实现卖票,代码步骤如下

    • 判断票数大于0,就卖票,并告知是哪个窗口卖的

    • 卖了票之后,总票数要减1

    • 票卖没了,线程停止

    • 定义一个测试类SellTicketDemo,里面有main方法,代码步骤如下

    • 创建SellTicket类的对象

    • 创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称

    • 启动线程

     

    代码如下:

     1 public class SellTicket implements Runnable {
     2     private int tickets = 100;
     3     //在SellTicket类中重写run()方法实现卖票,代码步骤如下
     4     @Override
     5     public void run() {
     6         while (true) {
     7             if(ticket <= 0){
     8                     //卖完了
     9                     break;
    10                 }else{
    11                     try {
    12                         Thread.sleep(100);
    13                     } catch (InterruptedException e) {
    14                         e.printStackTrace();
    15                     }
    16                     ticket--;
    17                     System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
    18                 }
    19         }
    20     }
    21 }
    22 public class SellTicketDemo {
    23     public static void main(String[] args) {
    24         //创建SellTicket类的对象
    25         SellTicket st = new SellTicket();
    26 
    27         //创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称
    28         Thread t1 = new Thread(st,"窗口1");
    29         Thread t2 = new Thread(st,"窗口2");
    30         Thread t3 = new Thread(st,"窗口3");
    31 
    32         //启动线程
    33         t1.start();
    34         t2.start();
    35         t3.start();
    36     }
    37 }

    卖票案例的问题:

    - 相同的票出现了多次

    - 出现了负数的票

    原因:线程执行的随机性导致的,可能在卖票过程中丢失cpu的执行权,导致出现问题

    同步代码块:

    synchronized(任意对象) {
    多条语句操作共享数据的代码
    }

     

    同步代码块的好处与坏处:

    • 好处:解决了多线程的数据安全问题
    • 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率

    代码演示:

     1 public class SellTicket implements Runnable {
     2     private int tickets = 100;
     3     private Object obj = new Object();
     4 
     5     @Override
     6     public void run() {
     7         while (true) {
     8             synchronized (obj) { // 对可能有安全问题的代码加锁,多个线程必须使用同一把锁
     9                 //t1进来后,就会把这段代码给锁起来
    10                 if (tickets > 0) {
    11                     try {
    12                         Thread.sleep(100);
    13                         //t1休息100毫秒
    14                     } catch (InterruptedException e) {
    15                         e.printStackTrace();
    16                     }
    17                     //窗口1正在出售第100张票
    18                     System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
    19                     tickets--; //tickets = 99;
    20                 }
    21             }
    22             //t1出来了,这段代码的锁就被释放了
    23         }
    24     }
    25 }
    26 
    27 public class SellTicketDemo {
    28     public static void main(String[] args) {
    29         SellTicket st = new SellTicket();
    30 
    31         Thread t1 = new Thread(st, "窗口1");
    32         Thread t2 = new Thread(st, "窗口2");
    33         Thread t3 = new Thread(st, "窗口3");
    34 
    35         t1.start();
    36         t2.start();
    37         t3.start();
    38     }
    39 }

     

    同步方法:

    同步方法:就是把synchronized关键字加到方法上:

    修饰符 synchronized 返回值类型 方法名(方法参数) {
    方法体;
    }

     

    同步方法的锁对象是this

    同步静态方法:

    同步静态方法:就是把synchronized关键字加到静态方法上

    修饰符 static synchronized 返回值类型 方法名(方法参数) {
    方法体;
    }

    同步静态方法的锁对象是:类名.class

    代码演示:

     1 public class MyRunnable implements Runnable {
     2     private static int ticketCount = 100;
     3 
     4     @Override
     5     public void run() {
     6         while(true){
     7             if("窗口一".equals(Thread.currentThread().getName())){
     8                 //同步方法
     9                 boolean result = synchronizedMthod();
    10                 if(result){
    11                     break;
    12                 }
    13             }
    14 
    15             if("窗口二".equals(Thread.currentThread().getName())){
    16                 //同步代码块
    17                 synchronized (MyRunnable.class){
    18                     if(ticketCount == 0){
    19                        break;
    20                     }else{
    21                         try {
    22                             Thread.sleep(10);
    23                         } catch (InterruptedException e) {
    24                             e.printStackTrace();
    25                         }
    26                         ticketCount--;
    27                         System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");
    28                     }
    29                 }
    30             }
    31 
    32         }
    33     }
    34 
    35     private static synchronized boolean synchronizedMthod() {
    36         if(ticketCount == 0){
    37             return true;
    38         }else{
    39             try {
    40                 Thread.sleep(10);
    41             } catch (InterruptedException e) {
    42                 e.printStackTrace();
    43             }
    44             ticketCount--;
    45             System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticketCount + "张票");
    46             return false;
    47         }
    48     }
    49 }

     

    Lock锁:


    虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock

    Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化

     

     

     

    - ReentrantLock构造方法

    | 方法名 | 说明 |
    | --------------- | -------------------- |
    | ReentrantLock() | 创建一个ReentrantLock的实例 |

    - 加锁解锁方法

    | 方法名 | 说明 |
    | ------------- | ---- |
    | void lock() | 获得锁 |
    | void unlock() | 释放锁 |

     

    代码如下:

     1  public class Ticket implements Runnable {
     2       //票的数量
     3       private int ticket = 100;
     4       private Object obj = new Object();
     5       private ReentrantLock lock = new ReentrantLock();
     6 
     7       @Override
     8       public void run() {
     9           while (true) {
    10               //synchronized (obj){//多个线程必须使用同一把锁.
    11               try {
    12                   lock.lock();
    13                   if (ticket <= 0) {
    14                       //卖完了
    15                       break;
    16                   } else {
    17                       Thread.sleep(100);
    18                       ticket--;
    19                       System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
    20                   }
    21               } catch (InterruptedException e) {
    22                   e.printStackTrace();
    23               } finally {
    24                   lock.unlock();
    25               }
    26               // }
    27           }
    28       }
    29   }
    30 
    31   public class Demo {
    32       public static void main(String[] args) {
    33           Ticket ticket = new Ticket();
    34 
    35           Thread t1 = new Thread(ticket);
    36           Thread t2 = new Thread(ticket);
    37           Thread t3 = new Thread(ticket);
    38 
    39           t1.setName("窗口一");
    40           t2.setName("窗口二");
    41           t3.setName("窗口三");
    42 
    43           t1.start();
    44           t2.start();
    45           t3.start();
    46       }
    47   }

    死锁

    概念:

    线程死锁是指由于两个或者多个线程互相持有对方所需要的资源,导致这些线程处于等待状态,无法前往执行

    产生死锁的情况:

    1. 资源有限

    2. 同步嵌套

    代码如下:

     1 public class Demo {
     2     public static void main(String[] args) {
     3         Object objA = new Object();
     4         Object objB = new Object();
     5 
     6         new Thread(()->{
     7             while(true){
     8                 synchronized (objA){
     9                     //线程一
    10                     synchronized (objB){
    11                         System.out.println("小康同学正在走路");
    12                     }
    13                 }
    14             }
    15         }).start();
    16 
    17         new Thread(()->{
    18             while(true){
    19                 synchronized (objB){
    20                     //线程二
    21                     synchronized (objA){
    22                         System.out.println("小薇同学正在走路");
    23                     }
    24                 }
    25             }
    26         }).start();
    27     }
    28 }

     

    迎风少年
  • 相关阅读:
    [Android] 升级了新的android studio之后 发生如下的报错,The following classes could not be instantiated:
    [IOS]译Size Classes with Xcode 6: One Storyboard for all Sizes
    [IOS] 利用@IBInspectable
    [IOS]swift自定义uicollectionviewcell
    [IOS]Swift使用SVGKit的记录
    [IOS初学]ios 第一篇 storyboard 与viewcontroller的关系
    [Android]关于filed 遍历资源文件的排序问题
    [Android]关于Installation error: INSTALL_PARSE_FAILED_MANIFEST_MALFORMED ,修改包名
    [Android]用图库打开指定的文件夹,没错是第一个画面直接是图库的文件夹画面
    [Android]新版的sdk中新建一个android应用,增加的PlaceholderFragment这个静态类发生的事情
  • 原文地址:https://www.cnblogs.com/ZYH-coder0927/p/13513777.html
Copyright © 2011-2022 走看看