Loading

同步

wait()是属于Object类的方法:
(1)首先,调用了wait()之后会引起当前线程处于等待状状态。
(2)其次,每个线程必须持有该对象的monitor。如果在当前线程中调用wait()方法之后,该线程就会释放monitor的持有对象并让自己处于等待状态。

sleep()方法来自于Thread类:
(1)首先,调用sleep()之后,会引起当前执行的线程进入暂时中断状态,也即睡眠状态。
(2)其次,虽然当前线程进入了睡眠状态,但是依然持有monitor对象。 (3)在中断完成之后,自动进入唤醒状态从而继续执行代码。
http://blog.csdn.net/kaka534/article/details/51849285

结论:
(1)在线程的运行过程中,调用该线程持有monitor对象的wait()方法时,该线程首先会进入等待状态,并将自己持有的monitor对象释放。
(2)如果一个线程正处于等待状态时,那么唤醒它的办法就是开启一个新的线程,通过notify()或者notifyAll()的方式去唤醒。当然,需要注意的一点就是,必须是同一个monitor对象。
(3)sleep()方法虽然会使线程中断,但是不会将自己的monitor对象释放,在中断结束后,依然能够保持代码继续执行。

判断多线程安全问题:
1、当前是否是一个多线程环境
2、是否有共享数据
3、似乎否有多条语句对共享数据进行操作
且只有第三点是人为可以改进的点。


同步机制
多条语句对共享数据进行包装用同步代码块
同步锁对象:每个进程都使用的对象,用synchronized锁对象,为了阻止两个进程同时对一共享资源并发访问,一般推荐用共享资源做同步锁对象(监视器):
synchronized{
代码块
}

同步锁对象可以是Object类型或者任意Java类对象


notify()这个方法,通过阅读源码我们可以总结一下几点: (1)当一个线程处于wait()状态时,也即等待它之前所持有的object’s
monitor被释放,通过notify()方法可以让该线程重新处于活动状态,从而去抢夺object’s monitor,唤醒该线程。
(2)如果多个线程同时处于等待状态,那么调用notify()方法只能随机唤醒一个线程。
(3)在同一时间内,只有一个线程能够获得object’s monitor,执行完毕之后,则再将其释放供其它线程抢占。

notifyAll()就是用来唤醒正在等待状态中的所有线程的,不过也需要注意以下几点:
(1)notifyAll()只会唤醒那些等待抢占指定object’s monitor的线程,其他线程则不会被唤醒。
(2)notifyAll()只会一个一个的唤醒,而并非统一唤醒。因为在同一时间内,只有一个线程能够持有object’s monitor
(3)notifyAll()只是随机的唤醒线程,并非有序唤醒。
http://blog.csdn.net/kaka534/article/details/51849285


举一个电影院窗口出票的例子,(未使用同步)

对象类:
package Day20_DuoJinCheng;
/**
 * @author Aoman_Hao
 */
public class UseRunnable2 implements Runnable{

    //static 静态修饰设置票数不能更改
    private static int tickets =20;


    @Override
    public void run() {
        //循环
        while(true){
                if(tickets > 0){
                    try {
                        //取票前等待100毫秒,并捕获异常
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"窗口正在出第"+(tickets--)+"张票");

            }

        }       
    }
}

测试类:
package Day20_DuoJinCheng;
/**
 * @author Aoman_Hao
 */
public class UseRunTest2 {

    public static void main(String[] args) {
        //通用Runnable
        UseRunnable2 ur = new UseRunnable2();

        //创建Thread对象
        Thread T1= new Thread(ur,"窗口1");
        Thread T2= new Thread(ur,"窗口2");
        Thread T3= new Thread(ur,"窗口3");

        //启动窗口对象
        T1.start();
        T2.start();
        T3.start();

    }

}
输出:
窗口2窗口正在出第20张票
窗口1窗口正在出第19张票
窗口3窗口正在出第19张票
窗口3窗口正在出第18张票
窗口2窗口正在出第16张票
窗口1窗口正在出第17张票
窗口2窗口正在出第15张票
窗口3窗口正在出第13张票
窗口1窗口正在出第14张票
窗口2窗口正在出第12张票
窗口3窗口正在出第11张票
窗口1窗口正在出第10张票
窗口3窗口正在出第9张票
窗口1窗口正在出第8张票
窗口2窗口正在出第9张票
窗口1窗口正在出第7张票
窗口2窗口正在出第5张票
窗口3窗口正在出第6张票
窗口3窗口正在出第4张票
窗口2窗口正在出第3张票
窗口1窗口正在出第4张票
窗口3窗口正在出第2张票
窗口2窗口正在出第1张票
窗口1窗口正在出第2张票

注:没有有序的完成售票,且出现重票的现象。

改进,加入同步机制

对象类:
package Day20_DuoJinCheng;
/**
 * @author Aoman_Hao
 */
public class UseRunnable2 implements Runnable{

    //static 静态修饰设置票数不能更改
    private static int tickets =20;

    //创建同步对象
    Object obj = new Object();

    @Override
    public void run() {
        //循环
        while(true){
                //同步锁对象
            synchronized (obj) {
                if(tickets > 0){
                    try {
                        //取票前等待100毫秒,并捕获异常
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"窗口正在出第"+(tickets--)+"张票");

            }
            }


        }       
    }
}
测试类同上。
输出:
窗口1窗口正在出第20张票
窗口1窗口正在出第19张票
窗口1窗口正在出第18张票
窗口1窗口正在出第17张票
窗口1窗口正在出第16张票
窗口1窗口正在出第15张票
窗口1窗口正在出第14张票
窗口1窗口正在出第13张票
窗口3窗口正在出第12张票
窗口3窗口正在出第11张票
窗口3窗口正在出第10张票
窗口3窗口正在出第9张票
窗口2窗口正在出第8张票
窗口2窗口正在出第7张票
窗口2窗口正在出第6张票
窗口3窗口正在出第5张票
窗口3窗口正在出第4张票
窗口3窗口正在出第3张票
窗口3窗口正在出第2张票
窗口3窗口正在出第1张票
posted @ 2017-12-05 15:17  AomanHao  阅读(7)  评论(0)    收藏  举报