线程安全:
如果有多个线程同时运行一段代码,并且运行结果与单线程运行结果是一样的,那么就说是线程安全的。
1.1 线程不安全案例
通过下面这个买票的案例我们会发现会出现两种情况
- 一张票卖了多次的情况
- 出现负票的情况
public class Client3 {
public static void main(String[] args) {
Ticket t = new Ticket();
//同时开启三个售票窗口
Thread thread = new Thread(t);
Thread thread2 = new Thread(t);
Thread thread3 = new Thread(t);
thread.start();
thread2.start();
thread3.start();
}
}
class Ticket implements Runnable {
//定义100张票
private int ticket = 100;
@Override
public void run() {
while (true) {
if (ticket < 0) {
return;
} else {
try {
//模拟出票动作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().toString() + "正在卖: " + ticket--);
}
}
}
}
1.2 通过线程同步的方式解决
当我们使用多个线程对资源进行写操作的时候,容易出现安全性问题,这里我们可以通过使用java的同步机制Synchronized来解决
1.2.1 同步代码块
Synchronized可以对使用的代码块进行互斥访问,这里我们只需要把代码区块添加synchronized即可实现同步
class Ticket implements Runnable {
//定义100张票
private int ticket = 100;
//添加一把锁
private Object lock = new Object();
@Override
public void run() {
while (true) {
//lock的类型是任意的
synchronized (lock) {
if (ticket < 0) {
return;
} else {
try {
//模拟出票动作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().toString() + "正在卖: " + ticket--);
}
}
}
}
}
1.2.2 同步方法
public class Ticket implements Runnable {
//定义100张票
private int ticket = 100;
@Override
public void run() {
while (true){
try {
selTicket();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 谁调用这个方法,锁对象就是谁(this)
*/
public synchronized void selTicket() throws InterruptedException {
if (ticket > 0) {
Thread.sleep(100);
} else {
return;
}
System.out.println("窗口" + Thread.currentThread().toString() + "票" + ticket--);
}
}
1.2.3 Lock锁
提供了比synchronized代码块和synchronized方法更广泛的锁定操作, 同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象
/**
* Lock接口中的方法
* lock
* unlock
* 使用步骤:
* Lock接口的实现类ReentrantLock
* 1. 在成员创建ReentrantLock的实现类
* 2. 在可能出现线程安全的位置开始调用lock()
* 3. 在可能出现线程安全的结束位置调用lock()
*/
public class RunnableImpl implements Runnable {
Lock lock = new ReentrantLock();
private int ticket = 20;
@Override
public void run() {
while (true){
lock.lock();
if (ticket >0 ){
try {
Thread.sleep(100);
System.out.println("当前的线程"+Thread.currentThread().toString()+" "+ticket--);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
}
}