多线程访问了共享的数据,会产生线程安全问题
public class RunnalbeImpl implements Runnable{
//定义一个多个线程共享的票源
private int ticket = 100;
//设置线程任务:买票
@Override
public void run() {
while (true) {
//判断票是否存在
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买第" + ticket + "张票");
ticket--;
}
}
}
}
public class Demo04Ticket {
public static void main(String[] args) {
/*
创建三个线程,同时开启,对共享的票进行出售
同一个实现类,多个线程,才会共享
*/
//创建Runnable接口的实现类对象
RunnalbeImpl run = new RunnalbeImpl();
//创建Thread类对象,传递上述实现类对象
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t0.start();
t1.start();
t2.start();
}
}
线程安全问题产生的原理
sleep使得线程放弃对CPU的执行权
t0, t1, t2同时执行到了一个程序代码(代码执行有时间,所以变化值的语句还没来得及执行,上一句就被多次执行)
线程安全问题是不能产生的,因此可以让一个线程在访问共享数据的时候,无论是否失去了CPU的执行权,其他的线程只能等待。
(保证始终一个线程在工作)
解决:
1.同步代码块
格式:
synchronized(同步锁) {
//同步代码块
//可能会出现线程安全的代码(访问了共享数据的代码)
}
注意:
- 通过代码块的锁对象,可以使用任意的对象
- 保证多个线程使用同一个锁对象
- 作用是:把同步代码块锁住,只让一个线程在同步代码块中执行
public class RunnalbeImpl implements Runnable{
//定义一个多个线程共享的票源
private int ticket = 100;
//创建一个锁对象,但是不能放run中,那样会产生多个锁对象
Object obj = new Object();
//设置线程任务:买票
@Override
public void run() {
while (true) {
synchronized (obj) {
//判断票是否存在
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买第" + ticket + "张票");
ticket--;
}
}
}
}
}
同步技术的原理
同步中的线程,没有执行完毕不会释放锁
同步外的线程,没有锁进不去同步
程序频繁的判断锁、获取锁、释放锁,程序的效率降低,但提高安全性
2.同步方法
格式
修饰符 synchronized 返回值类型 方法名(参数列表) {
//可能会出现线程安全问题的代码(访问了共享数据的代码)
}
this是创建对象之后产生的,静态方法优先于对象
3.Lock锁
java.util.concurrent.locks.lock接口
使用步骤:
- 在成员位置创建一个ReentrantLock对象
- 可能会出现安全问题的代码前调用Lock接口中的方法lock获取锁
- 可能会出现安全问题的代码后调用Lock接口中的方法unlock释放锁
public class RunnalbeImpl implements Runnable{
//定义一个多个线程共享的票源
private int ticket = 100;
//step 1
Lock l = new ReentrantLock();
//设置线程任务:买票
@Override
public void run() {
while (true) {
//step 2
l.lock();
//判断票是否存在
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "买第" + ticket + "张票");
ticket--;
}
//step 3
l.unlock();
}
}
}
下面优化:无论是否发生异常,都会把锁释放掉
(释放锁放入finally)
public class RunnalbeImpl implements Runnable{
//定义一个多个线程共享的票源
private int ticket = 100;
//step 1
Lock l = new ReentrantLock();
//设置线程任务:买票
@Override
public void run() {
while (true) {
//step 2
l.lock();
//判断票是否存在
if (ticket > 0) {
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "买第" + ticket + "张票");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//step 3
//这样无论程序是否出现异常,都会把锁释放掉
l.unlock();
}
ticket--;
}
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。