解决线程不安全问题

以售票员售票为例

线程不安全体现

public class Demo3 {
    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 (true) {
                if (count > 0) {
                    //卖票
                    System.out.println("正在准备卖票");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + "出票成功,余票" + count);

                } else {
                    break;
                }
            }
        }
    }
}

结果:

正在准备卖票
正在准备卖票
正在准备卖票
Thread-1出票成功,余票8
正在准备卖票
Thread-0出票成功,余票7
正在准备卖票
Thread-2出票成功,余票9
正在准备卖票
Thread-2出票成功,余票6
Thread-0出票成功,余票5
正在准备卖票
正在准备卖票
Thread-1出票成功,余票4
正在准备卖票
Thread-1出票成功,余票3
正在准备卖票
Thread-2出票成功,余票2
正在准备卖票
Thread-0出票成功,余票1
正在准备卖票
Thread-2出票成功,余票0
Thread-1出票成功,余票-1
Thread-0出票成功,余票-2

Process finished with exit code 0

解决方案1.线程同步:synchronized

同步代码块

可以想象为在改对象上上了一把锁

1.锁可以是任意的类型

2.多个线程对象要使用同一把锁

任何时候都最多允许一个对象拥有同步锁谁拿到锁就谁进入同步代码块

格式: synchronized(锁对象) {

 

}

public class Demo1 {
    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;
        private Object o = new Object();
        @Override
        public void run() {
            while (true) {
                synchronized (o) {
                    if (count > 0) {
                        //卖票
                        System.out.println("正在准备卖票");
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName() + "出票成功,余票" + count);
                    } else {
                        break;
                    }
                }
            }
        }
    }
}

结果:

正在准备卖票
Thread-0出票成功,余票9
正在准备卖票
Thread-0出票成功,余票8
正在准备卖票
Thread-2出票成功,余票7
正在准备卖票
Thread-1出票成功,余票6
正在准备卖票
Thread-2出票成功,余票5
正在准备卖票
Thread-0出票成功,余票4
正在准备卖票
Thread-2出票成功,余票3
正在准备卖票
Thread-1出票成功,余票2
正在准备卖票
Thread-2出票成功,余票1
正在准备卖票
Thread-2出票成功,余票0

Process finished with exit code 0

解决方案2.同步方法

使用synchronized修饰的方法叫做同步方法,保证线程安全,当a线程执行该方法的时候,其他线程只可以在方法外等待。

public class Demo2 {
    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 (true) {
                boolean flag = sale();
                if (!flag) {
                    break;
                }
            }
        }
        public synchronized boolean sale() {
            if (count > 0) {
                //卖票
                System.out.println("正在准备卖票");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName() + "出票成功,余票" + count);
                return true;
            }
            return false;
        }
    }
}

结果:
 

正在准备卖票
Thread-0出票成功,余票9
正在准备卖票
Thread-0出票成功,余票8
正在准备卖票
Thread-1出票成功,余票7
正在准备卖票
Thread-1出票成功,余票6
正在准备卖票
Thread-2出票成功,余票5
正在准备卖票
Thread-1出票成功,余票4
正在准备卖票
Thread-0出票成功,余票3
正在准备卖票
Thread-0出票成功,余票2
正在准备卖票
Thread-1出票成功,余票1
正在准备卖票
Thread-1出票成功,余票0

Process finished with exit code 0

解决方案2.显式锁 Lock 子类 ReentrantLock

java.util.concurrent.locks.Lock 机制提供了比synchronized代码块和synchronized方法更广泛的锁定操作, 同步代码块/同步方法具有的功能Lock都有,除此之外更强大,更体现面向对象。

Lock锁的功能

public void lock()加同步锁

public void unlock() 释放同步锁

同步代码块 和 同步方法都是隐式锁

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo3 {
    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;
        //显式锁
        Lock l = new ReentrantLock();
        @Override
        public void run() {
            while (true) {
                l.lock();
                if (count > 0) {
                    //卖票
                    System.out.println("正在准备卖票");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    count--;
                    System.out.println(Thread.currentThread().getName() + "出票成功,余票" + count);
                } else {
                    break;
                }
                l.unlock();
            }
        }
    }
}

结果:
 

正在准备卖票
Thread-0出票成功,余票9
正在准备卖票
Thread-2出票成功,余票8
正在准备卖票
Thread-2出票成功,余票7
正在准备卖票
Thread-2出票成功,余票6
正在准备卖票
Thread-1出票成功,余票5
正在准备卖票
Thread-1出票成功,余票4
正在准备卖票
Thread-0出票成功,余票3
正在准备卖票
Thread-2出票成功,余票2
正在准备卖票
Thread-2出票成功,余票1
正在准备卖票
Thread-2出票成功,余票0

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴帅345

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值