• 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 }
  • 相关阅读:
    吴裕雄--天生自然--Go 语言学习笔记--Go 语言指针
    SVN报错“Failed to run the WC DB work queue associated with”解决办法
    【Oracle学习笔记】Oralce随机数与随机取样
    [CUDA]CUDA编程资源汇总
    [算法竞赛]目标检测常用技巧总结
    [CUDA]CUDA编程实战五——dot向量点积运算
    [CUDA]CUDA编程实战四——矩阵乘法
    [CUDA]CUDA编程实战三——矩阵加法的实现
    [CUDA]CUDA编程实战一——了解CUDA及获取GPU信息
    [CUDA]CUDA编程实战二——向量加法
  • 原文地址:https://www.cnblogs.com/whwjava/p/8943761.html
Copyright © 2020-2023  润新知