2020-09-15 Java线程安全

2020-09-16  本文已影响0人  flynnny

一、线程安全简介

单线程程序不会产生线程安全问题。
多线程程序没有访问共享数据,也不会产生问题。
多线程程序访问共享数据,会产生线程安全问题。

三个窗口卖票Demo:


线程安全1.png
线程安全2.png

线程安全问题(出现了重复的票不存在的票)产生原因:
假设t0、t1、t2都执行进if后立即失去话语权,这时再随机醒来,就会打出
“tx...正在卖第1张票”(此时ticket-- 即-0)循环结束
“ty...正在卖第0张票”(此时ticket-- 即-1)循环结束
“tz...正在卖第-1张票”(此时ticket-- 即-2)循环结束
假设t0、t1、t2都执行到输出语句时立即失去话语权,这时再随机醒来,就会打出卖出了相同的票。

二、线程同步

为了解决每个子线程都能正常进行原子操作的安全问题,Java提供了同步机制(synchronized)来解决。
有三种方式:
1.同步代码块;
2.同步方法;
3.锁机制。

2.1同步代码块(对某个区域中的资源实行互斥访问)

格式:

synchronized(同步锁){
        //需要同步的代码
}

同步锁:在对象上标记了一个锁,可以使任意类型;多个线程对象要使用同一把锁。(注意:在任何时候,最多允许一个线程拥有同步锁,谁拿锁谁就进入代码块,其他线程只能等待BLOCKED)
如下图,只要把imp类如此修改即可解决上面安全问题:

线程安全3.png

原理:
三个线程一起抢夺CPU执行权,谁抢到了谁卖票。
1.t0抢到了执行run方法,遇到synchronized代码块,这时会检查synchronized同步代码块是否有锁对象;
发现有(因为是第一个执行),t0就会获得锁对象,进入执行。

2.t1抢到了执行run方法,遇到synchronized代码块,这时会检查synchronized同步代码块是否有锁对象;
发现没有,进入到阻塞状态,一直等待t0线程归还所对象。
一直到t0执行完同步中的代码,会归还锁对象给同步代码块,t1才能获得锁对象进入其中执行。

同步中的线程没有执行完毕不会释放锁,同步外的线程没有锁进不去同步。保证了安全,程序频繁的判断锁、获取锁、释放锁、程序效率会降低

2.2同步方法

格式:

puvlic synchronized void method(){
        //需要同步的代码
}

此时的同步锁:对于非static方法,同步锁就是this;对于static方法,同步锁为当前方法所在类的字节码对象(类名.class)

线程安全4.png
2.3同步锁

JDK1.5之后提供了更强大的Lock锁(同步锁),可获得更广发的操作。
java.util.concurrent.locks.Lock接口:
public void lock():加锁
public void unlock():释放锁
使用:java.util.concurrent.locks.Lock.ReentrantLock
1.创建ReentrantLock对象
2.在可能出现安全问题的代码前调用 lock();
3.在可能出现安全问题的代码后调用unlock();(建议写在finally中

线程安全5.png

下一章介绍线程的状态和等待唤醒机制

上一篇下一篇

猜你喜欢

热点阅读