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