zoukankan      html  css  js  c++  java
  • JAVA Class23

    学习内容:

    1.线程同步:

    如果同时有多个线程对某个元素进行操作,可能会产生与预期不符的效果、数据

    public  class Tickets implements Runnable{
        static int tickets = 100;
        @Override
        public void run() {
            while(true) {
                if(tickets>0) {
                    try {
                        Thread.sleep(100);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                        System.out.println(Thread.currentThread().getName()+"出售第"+(tickets--)+"张票");
                }else {
                    return;
                }
            }
        }    
        }
    }
    
    public class Synch {
        
        public static void main(String[] args) {
            Tickets t = new Tickets();
            Thread app = new Thread(t);--
            Thread online = new Thread(t);
            Thread scene = new Thread(t);
            app.start();
            online.start();
            scene.start();
       }
    }

    上面这段代码运行到最后,可能会产生同一个编号的票被出售多次,或者出现0号票、-1号票,出现这种情况的原因:

    同时有多个线程对tickets--,假设当tickets等于1的时候,一号线程进行操作,if判断,符合条件,售出,数量减1,同时二号线程执行run,因为一号线程还没执行完,tickets--还没被执行,二号线程的if条件也是满足的,但如果一号线程完成了操作,此时的tickets已经为0,但是二号线程的if判断已经完成,所以会继续执行,出现了0号票,同理如果同时三后线程也在进行操作,则会出现-1号票。

    解决方式:

    A.synchronized锁

    (1)新建一个Object,synchronized (Object)加锁,同时只允许一个线程进行操作。

    public  class Tickets implements Runnable{
        static int tickets = 100;
        Object obj = new Object();
        @Override
        public void run() {
            while(true) {
                synchronized (obj){//加锁,同时只允许一个线程获取当前对象
                    if(tickets>0) {
                        try {
                            Thread.sleep(100);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName()+"出售第"+(tickets--)+"张票");
                    }else {
                        return;
                    }
                }
            }    
        }
    }
    
    public class Synch {
        
        public static void main(String[] args) {
            Tickets t = new Tickets();
            Thread app = new Thread(t);
            Thread online = new Thread(t);
            Thread scene = new Thread(t);
            app.start();
            online.start();
            scene.start();
       }
    }

    (2)synchronized方法

    注意,如果是普通方法,则synchronized的对象是当前对象,相当于this,如果是类方法,则synchronized的对象是当前类

    public  class Tickets implements Runnable{
        static int tickets = 100;
        @Override
        public void run() {
            while(true) {
                if(method()) {
                }else {
                    return;
                }
            }
        }
        public synchronized static boolean method() {//修饰静态方法,锁当前类,修饰对象方法,锁当前对象
            boolean flag = true;
            if(tickets>0) {
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"出售第"+(tickets--)+"张票");
            }else {
                flag = false;
            }
            return flag;
        }
    }

    B.Lock锁

    区别:

    1. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现,Lock是代码层面的实现。
    2. Lock可以选择性的获取锁,如果一段时间获取不到,可以放弃。synchronized不行,会一根筋一直获取下去。 借助Lock的这个特性,就能够规避死锁,synchronized必须通过谨慎和良好的设计,才能减少死锁的发生。
    3. synchronized在发生异常和同步块结束的时候,会自动释放锁。而Lock必须手动释放, 所以如果忘记了释放锁,一样会造成死锁。

    public class Locked implements Runnable{
        static int tickets = 100;
        Object obj = new Object();
        Lock lock = new ReentrantLock();
        @Override
        public void run() {
            while(true) {
                lock.lock();
                try {
                    Thread.sleep(100);
                        if(tickets>0) {
                            System.out.println(Thread.currentThread().getName()+"出售第"+(tickets--)+"张票");
                        }else {
                            return;
                        }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }    
        }
    }
    
    public class Locked implements Runnable{
        static int tickets = 100;
        Object obj = new Object();
        Lock lock = new ReentrantLock();
        @Override
        public void run() {
            boolean locked = false;
            while(true) {
                lock.lock();
                try {
                    locked=lock.tryLock(1,TimeUnit.SECONDS);//一秒内获取不到就放弃
                    Thread.sleep(100);
                    if(tickets>0) {
                        System.out.println(Thread.currentThread().getName()+"出售第"+(tickets--)+"张票");
                    }else {
                        return;
                    }
                } catch (InterruptedException e1) {
                    // TODO Auto-generated catch block
                    e1.printStackTrace();
                } finally {
                    if(locked) {
                        lock.unlock();
                    }
                }
            }    
        }
    }

    2.线程交互

    通过wait方法以及notify、notifyAll来实现线程的休眠与唤醒,例如当某个条件满足时,当前线程等待,通过另一个线程的在来唤醒它。

    下面这段代码可以实现“锁血”,英雄血量变为1时,使用减血方法的线程wait,当加血线程加血时,再把休眠的线程唤醒:

     1 public class Hero {
     2     public String name;
     3     public float hp;
     4     public int damage;
     5  
     6     public synchronized void recover() {
     7         hp = hp + 1;
     8         System.out.printf("%s 回血1点,增加血后,%s的血量是%.0f%n", name, name, hp);
     9         // 通知那些等待在this对象上的线程,可以醒过来了,如第17行,等待着的减血线程,苏醒过来
    10         this.notify();
    11     }
    12  
    13     public synchronized void hurt() {
    14         if (hp == 1) {
    15             try {
    16                 // 让占有this的减血线程,暂时释放对this的占有,并等待
    17                 this.wait();
    18             } catch (InterruptedException e) {
    19                 e.printStackTrace();
    20             }
    21         }
    22         hp = hp - 1;
    23         System.out.printf("%s 减血1点,减少血后,%s的血量是%.0f%n", name, name, hp);
    24     }
    25 
    26 }
    27 
    28 public class Synch {
    29     
    30     public static void main(String[] args) {
    31         final Hero gareen = new Hero();
    32         gareen.name = "盖伦";
    33         gareen.hp = 616; 
    34         Thread t1 = new Thread(){
    35             public void run(){
    36                 while(true){
    37                     gareen.hurt();
    38                     try {
    39                         Thread.sleep(10);
    40                     } catch (InterruptedException e) {
    41                         e.printStackTrace();
    42                     }
    43                 }
    44    
    45             }
    46         };
    47         t1.start();
    48    
    49         Thread t2 = new Thread(){
    50             public void run(){
    51                 while(true){
    52                     gareen.recover();
    53                     try {
    54                         Thread.sleep(50);
    55                     } catch (InterruptedException e) {
    56                         e.printStackTrace();
    57                     }
    58                 }
    59    
    60             }
    61         };
    62         t2.start();
    63              
    64     }
    65 }
  • 相关阅读:
    UART中的硬件流控RTS与CTS
    Yii smsto短信接口的函数
    分布式选主 利用Mysql ACID和Lease协议实现选主和高可用
    Extjs GridPanel 合计功能 解决滚动条滚动问题和页面刷新滚动条回到初始位置问题。
    基于ARM+LINUX的无线视频采集系统设计搭建嵌入式web服务器
    C++里父类的析构函数为什么声明为virtual
    重量过载 = 重载
    Spread Studio中文支持图解
    Android用户如何FQ访问被封锁的媒体资源
    将myeclipse 10.x以下版本web project的导入到myeclipse blue 2013 部署没有项目名
  • 原文地址:https://www.cnblogs.com/whwjava/p/8943761.html
Copyright © 2011-2022 走看看