多线程的安全性问题
上一节中,虽然我们已经完成了卖票程序,但是其实隐藏了一个非常严重的安全问题,我们看代码这段代码:
class Ticket implements Runnable{
private int tick=100;
public void run(){
while (true){
if(tick>0){ System.out.println(Thread.currentThread().getName()+"....sale"+tick--);
}
}
}
}
当票剩余一张时,在我们的if(tick>0)的时候,我们的0线程进入,这时候0线程进入暂时状态,1线程进入,因为此时tick还未变化, 如果这个时候1线程也进入暂时状态。这个时候就会出现卖出0张票和-1张票的情况。
我们可以模拟下这个情况:
class Ticket implements Runnable{
private int tick=100;
public void run(){
while (true){
if(tick>0){
try{
Thread.sleep(100);
}catch(Exception e){
}
}
System.out.println(Thread.currentThread().getName()+"....sale"+tick--);
}
}
}
}
此时我们运行发现:
多线程的运行出了安全问题。
当多个线程在操作同一个共享数据时, 一个线程对多余语句只执行了一部分,还没执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法: 对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
java对于多线的安全问题提供了专业的解决方式, 就是同步代码块。
synchronized(对象)
{
需要同步的代码块
}
改正后的代码
class Ticket implements Runnable {
private int tick = 100;
Object o=new Object(); //锁
public void run() {
while (true) {
synchronized (o) {
if (tick > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "....sale" + tick--);
}
}
}
}
}
原理,锁对象具有一个标志位,当线程进来时,将标志位置成0,其他线程进不来,出去后,标志位重新置成1,其他线程就可以进来了。
对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使获取了cup的执行权,也进不去, 因为没有获取锁。
同步的前提:
1.必须要有两个或者两个以上的线程。
2.必须是多个线程使用同一个锁。
必须保证同步中只有一个线程在运行。
好处:解决了多线程的安全问题;
弊端:多个线程需要判断锁,较为消耗资源。
同步函数:
我们可以让函数具有同步的功能,只需要在定义函数的时候加上synchronized,此时该函数就具备同步代码块的功能,线程安全。
public synchronized void add (int n){
sum=sum+n;
}
同步函数用的是哪一个锁呢?
函数需要被对象调用,那么函数都有一个所属对象引用,就是this。所以同步函数使用的锁是this。
上一篇: java——添加大量学生用户
下一篇: 线程的安全性问题