zoukankan      html  css  js  c++  java
  • 线程安全 -同步锁机制

    package cn.learn.thread.ThreadSafe;
    
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    /*
        实现Runnable必须重写run()方法
        安全问题解决方案:synchronized同步机制 -在共享数据操作的位置进行设置run()
            过程:
                3个线程一起抢占cpu的执行权,抢占成功,该线程执行run()方法,遇到synchronized代码块,
                这时该线程会检查是否存在锁对象
                有:就会获取到锁对象进入同步中执行
                没有:(被前面的线程抢占)就会进入阻塞状态,会一直等待上一线程结束归还锁对象,获取到锁对象进入同步执行
            总结:同步中的线程,没有执行完毕就不会释放锁对象,同步外的线程,没有锁对象就进不去内部
               同步锁锁的是线程,锁的不是线程抢占cpu执行权
            问题:频繁的判断锁,获取锁访问锁,程序效率降低
    
     */
    public class RunnableImpl implements Runnable {
    
        //第二种方法
        //定义一个多线程共享资源
        private static int tickets=100;
    
        //第一种方法
        //创建锁对象,同步锁/对象锁/对象监视器
        Object obj = new Object();
    
        //第三种方法
        Lock lck = new ReentrantLock();
    
    
        @Override
        public void run() {
    
            //第三种方法,Lock
            lck.lock(); //获取锁
    
            try {
                for (; tickets > 0; tickets--) {
                    Thread.sleep(10);
                    System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                //无论是否异常,均释放锁,提高程序效率
                lck.unlock(); //释放锁
            }
        }
    
    //     //第二种方法,同步方法
    //        payTicket();
    
           //第一种方法
    //     //等当前进程访问完,才允许其他进程访问,但是其他线程存在的意义在哪呢?  -使用线程通信,等待唤醒机制
    //        synchronized (obj) {
    //            for (; tickets > 0; tickets--) {
    ///*            //程序睡眠,会提高不安全概率
    //            try {
    //                Thread.sleep(10000);
    //            } catch (InterruptedException e) {
    //                e.printStackTrace();
    //            }
    //*/
    //                System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
    //            }
    //        }
    
    
        //同步方法保证线程安全 -把方法内部代码块锁住只让一个线程运行,锁对象是this即RunnableImpl
    //    public synchronized void payTicket(){
    //        for (; tickets > 0; tickets--) {
    //            System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
    //        }
    //    }
    
    
    
        //静态同步方法,锁对象不再是this。this为创建对象后生成,静态方法优先于对象
        //锁对象是本类的class属性,class文件对象(反射)
    //    public static synchronized void payTicket(){
    //        for (; tickets > 0; tickets--) {
    //            System.out.println(Thread.currentThread().getName() + "正在卖第" + tickets + "张票");
    //        }
    //    }
    
    
    
    }
    package cn.learn.thread.ThreadSafe;
    /*
        模拟多线程开始卖票
    
        线程安全问题:出现了不存在的值和重复的值
        原因:   实际是多线程在抢占cpu时,会引起变量的值在通过校验后变化,然后输出负数
                而相同值的出现,只能说明多个线程同时执行到了输出语句,而变量变化的语句还未执行
    
                注:线程安全问题是不允许产生的,我们可以让一个线程在访问共享数据时,无论当前
                线程是否失去了cpu的执行权,都应该让其他线程进行等待,直到当前线程执行完,再进行下一个线程
        解决方案:
            1.  synchronized同步机制,在线程执行到共享数据处加锁
            格式:
                    synchronized(锁对象){
                        可能出现安全问题的代码(访问了共享数据的代码)
                    }
                注:锁对象可以任意对象,但是必须保证多个线程使用的锁对象是同一个
                    锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行
           2.使用同步方法
              使用步骤:
                    1.把访问了共享数据的代码抽取出来,放到一个方法中
                    2.添加修饰符,synchronized
                注:synchronized锁的对象是this,实际是第一种方法用synchronized(this)
    
           静态方法保证安全,在第二种方法前加static修饰符
               注:静态方法里的变量也必须是静态的
                  锁对象是本类的class属性,class文件对象(反射),即synchronized(Runnable.class)
    
           3.Lock锁,在java.util.concurrent.locks.Lock中,可以看见什么时候释放获取的锁
              lock接口方法:void lock();获取锁
                   void unlock();释放锁
              实现类:java.util.concurrent.locks.ReentrantLock implements Lock   Reentrant -可再进入的
              使用步骤:
                    1.在成员位置创建一个ReentrantLock对象
                    2.在可能出现安全问题的代码前调用Lock接口中的方法lock获取锁
                    3.在可能出现安全问题的代码前调用Lock接口中的方法unlock释放锁
    
     */
    public class Test {
        public static void main(String[] args) {
            //一个实现类
            RunnableImpl sale1 = new RunnableImpl();
    
    
            //生成多个线程并共享一个实现类资源,执行
            new Thread(sale1,"售票员线程1").start();
            new Thread(sale1,"售票员线程2").start();
            new Thread(sale1,"售票员线程3").start();
            //注:如不解决线程问题,出现两个售票卖第100张票,安全问题出现
    
        }
    
    }
  • 相关阅读:
    Android之TabHost使用(引用SDK例子文档)
    Android之在线词典
    校验插入指定结点是否导致编码循环的示例函数
    查表法按日期生成流水号的示例.sql
    备份数据库并提供下载的asp文件
    Metro风格XAML应用程序性能技巧
    导入文本文件时如何指定字段类型.sql
    宝塔形数据的处理.sql
    将某个目录上的Excel表,导入到数据库中.sql
    Using SqlDataReader’s new async methods in .Net 4.5 Beta
  • 原文地址:https://www.cnblogs.com/huxiaobai/p/11524924.html
Copyright © 2011-2022 走看看