java中解决线程安全问题的三种方法
当我们使用线程进行卖票时会出现诸多的安全问题为了解决这种情况,有三种方法可以进行选择
第一种是同步代码块
第二种是同步方法
第三种是Lock锁
在没有用的解决安全问题的时候会出现以下问题
测试类
public class demotest {
public static void main(String[] args) {
MyRunnable myRunnable01 = new MyRunnable();
new Thread(myRunnable01,"张三").start();
new Thread(myRunnable01,"李四").start();
new Thread(myRunnable01,"王五").start();
// new Thread(myRunnable01).start();
}
}
Runnable接口的实现类
public class MyRunnable implements Runnable {
private int ticket=100;
Object object=new Object();
@Override
public void run() {
while (true){
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->正在卖第"+(101-ticket)+"张票");
ticket--;
}else {
System.out.println("对不起,票已售光!");
break;
}
}
}
}
输出
张三--->正在卖第1张票
王五--->正在卖第1张票
李四--->正在卖第1张票
王五--->正在卖第4张票
张三--->正在卖第4张票
李四--->正在卖第6张票
王五--->正在卖第7张票
发现会出现售卖同一张票的情况,这是因为在cpu进行多线程选择时,他们已经进入了run()方法进行了if语句的判断,所以在if语句之后的ticket- -并不能影响已经输出的语句。
这里就需要用到同步的概念了,学过操作系统的应该会很熟悉。
第一种方法:同步代码块
同步代码块 : synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。
格式:
synchronized(同步锁){
需要同步操作的代码
}
同步锁:
对象的同步锁只是一个概念,可以想象为在对象上标记了一个锁.
- 锁对象 可以是任意类型。
- 多个线程对象 要使用同一把锁。
实例:Runnable接口实现类
private int ticket=100;
Object object=new Object();定义锁对象
@Override
public void run() {
while (true){
synchronized (object){
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->正在卖第"+(101-ticket)+"张票");
ticket--;
}else {
System.out.println("对不起,票已售光!");
break;
}
}
}}
}
输出
张三--->正在卖第1张票
张三--->正在卖第2张票
王五--->正在卖第3张票
王五--->正在卖第4张票
王五--->正在卖第5张票
王五--->正在卖第6张票
王五--->正在卖第7张票
在这里可以看到输出的结果很正常
第二种方法:同步方法
同步方法 :使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
格式:
public synchronized void method(){
可能会产生线程安全问题的代码
}
同步锁是谁?
对于非static方法,同步锁就是this。
对于static方法,我们使用当前方法所在类的字节码对象(类名.class)。
使用步骤:
1.把访问了共享数据的代码抽取出来,放到一个方法中
2.在方法上添加synchronized修饰符
案例Runnable的实现类
public class MyRunnable implements Runnable {
private int ticket=100;
@Override
public void run() {
while (true){
method();
}
}
public synchronized void method(){
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->正在卖第"+(101-ticket)+"张票");
ticket--;
}
}
}
重新定义一个带有synchronized修饰符的方法将需要进行同步的代码给框起来,之后再run方法中在进行调用方法即可。
第三种方法:Lock锁
java.util.concurrent.locks.Lock
机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作,
同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。
Lock锁也称同步锁,加锁与释放锁方法化了,如下:
public void lock()
:加同步锁。
public void unlock()
:释放同步锁。
使用步骤:
1.在成员位置创建一个ReentrantLock对象。
2.在可能会出现安全问题的代码前调用Lock接口中的方法Lock获取锁。
3.在可能会出现安全问题的代码后调用Lock接口中的方法unLock释放锁。
案例Runnable的实现类
public class MyRunnable implements Runnable {
private int ticket=100;
Lock l=new ReentrantLock();
@Override
public void run() {
while (true){
l.lock();//加同步锁
if (ticket>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->正在卖第"+(101-ticket)+"张票");
ticket--;
}else {
System.out.println("对不起,票已售光!");
break;
}
l.unlock();//释放锁
}
}
}