线程安全 -同步锁机制

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张票,安全问题出现

    }

}

 

上一篇:HDU - 1260 Tickets(简单 dp)


下一篇:【LeetCode】重新安排行程