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