您的位置:首页 > 其它

多线程-线程安全问题

2020-08-18 17:58 483 查看

线程安全问题

什么是线程安全问题?
答:看下面这个例子,有时候执行出来余票会出现-1或-2,虽然我们已经在循环的时候限制了余票(count>0),但是依然会出现这个问题,这就是线程安全问题。

//因线程不安全,出现问题的案例
public class Demo7 {
public static void main(String[] args) {
//线程不安全
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}

static class Ticket implements Runnable{
//总票数
private int count = 10;
@Override
public void run() {
while (count>0){
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println("卖票结束,余票:"+count);
}
}
}
}

这个问题是如何产生的?
分析:三个进程都进入到while循环,但count数量已经不能满足三个线程去减依然大于0,然后就会出现count等于-1或-2的情况,显然这不是我们想要的结果。
那么如何去解决这个问题呢?
答:要解决问题首先要分析产生的原因,上面我们已经分析过了产生原因,这段代码是因为while(count > 0)这个判断与count - -之间的间隔部分被其他进程闯入导致的,那我们只要不让while(count > 0)与count - -中间执行的间隔进入别的进程就行了。
思路很简单那么怎么实现,这个可以实现吗?
答:下面就来看下怎么实现这个想法

解决方案1-同步代码块

synchronized (锁对象) {锁上的代码块}

//线程同步synchronized

public class Demo8 {
public static void main(String[] args) {
Object o = new Object();
//线程不安全
//解决方案1  同步代码块
//格式:synchronized(锁对象){
//
//
//      }
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}

static class Ticket implements Runnable{
//总票数
private int count = 10;
private Object o = new Object();
@Override
public void run() {
//Object o = new Object();    //这里不是同一把锁,所以锁不住
while (true) {
synchronized (o) {
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
}else {
break;
}

}
}
}
}
}

注意:需要用同一把锁才可以锁上

解决方案2-同步方法

方法权限修饰符后加synchronized

//线程同步synchronized

public class Demo9 {
public static void main(String[] args) {
Object o = new Object();
//线程不安全
//解决方案2  同步方法
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}

static class Ticket implements Runnable{
//总票数
private int count = 10;
@Override
public void run() {

while (true) {
boolean flag = sale();
if(!flag){
break;
}
}
}
public synchronized boolean sale(){
if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
return true;
}
return false;

}
}
}
  • 如果是是静态方法锁对象是类名.class
  • 如果不是静态方法锁对象是this
  • 不同对象对应的锁不同
//锁不同
new Thread(new Thread()).start();
new Thread(new Ticket()).start();
new Thread(new Thread()).start();
//锁相同
Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
  • 如果同步代码块锁对象是this,则下面的同步方法也被上锁。
//可以锁住后面的同步方法
synchronized (this){
}

解决方案3-显示锁Lock

  • 同步代码块和同步方法都属于隐式锁

创建锁对象

Lock l = new ReentrantLock(false);

上锁

l.lock();

解锁

l.unlock();
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//同步代码块和同步方法都属于隐式锁
//线程同步lock

public class Demo10 {
public static void main(String[] args) {
Object o = new Object();
//线程不安全
//解决方案3   显示锁  Lock  子类 ReentrantLock

Runnable run = new Ticket();
new Thread(run).start();
new Thread(run).start();
new Thread(run).start();
}

static class Ticket implements Runnable{
//总票数
private int count = 10;
//参数为true表示公平锁    默认是false 不是公平锁
private Lock l = new ReentrantLock();
@Override
public void run() {
while (true) {
l.lock();if (count > 0) {
//卖票
System.out.println("正在准备卖票");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
count--;
System.out.println(Thread.currentThread().getName()+"卖票结束,余票:" + count);
}else {
break;
}
l.unlock();
}
}
}
}
  • 非公平锁:(抢占)上面三种都是非公平锁
  • 公平锁:(排队)
//参数为true表示公平锁    默认是false 不是公平锁
Lock l = new ReentrantLock(true);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: