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 }

猜你喜欢

转载自www.cnblogs.com/whwjava/p/8943761.html