🌺个人主页:Dawn黎明开始
🎀系列专栏:Java
⭐每日一句:等风来,不如追风去📢欢迎大家:关注🔍+点赞👍+评论📝+收藏⭐️
文章目录
电影院上映一部电影,共有三个窗口,请你设计一个模拟电影院卖票的程序。
代码如下👇🏻
- package Process3;
-
- public class SellTicket implements Runnable {
-
- private int tickets = 20;//总票数
- private int ticketId = 1;//票号
-
- @Override
- public void run() {
- while (tickets>0) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
- tickets--; //剩余票数--
- ticketId++; //票号++
- }
- }
- }
- package Process3;
-
- public class SellTicketDemo {
- public static void main(String[] args) {
- // 创建资源对象
- SellTicket st = new SellTicket();
-
- // 创建三个线程对象
- Thread t1 = new Thread(st, "窗口1");
- Thread t2 = new Thread(st, "窗口2");
- Thread t3 = new Thread(st, "窗口3");
-
- // 启动线程
- t1.start();
- t2.start();
- t3.start();
- }
- }
运行结果👇🏻
总结:该方法不可以,有重票和漏票和多票问题。
线程安全问题其实就是由多个线程同时处理共享资源所导致的。要想解决线程安全问题,必须得保证处理共享资源的代码在任意时刻只能有一个线程访问。为此,Java中提供了线程同步机制。
原理
(1).当线程执行同步代码块时,首先会检查lock锁对象的标志位。
(2).默认情况下标志位为1,此时线程会执行Synchronized同步代码块,同时将锁对象的标志位置为0。
(3).当一个新的线程执行到这段同步代码块时,由于锁对象的标志位为0,新线程会发生阻塞,等待当前线程执行完同步代码块后。
(4).锁对象的标志位被置为1,新线程才能进入同步代码块执行其中的代码,这样循环往复,直到共享资源被处理完为止。
同步代码块:
* synchronized(对象){
* 需要同步的代码;
* }
*
* (1).对象是什么呢?
* 我们可以随便创建一个对象试试。
* (2).需要同步的代码是哪些呢?
* 把多条语句操作共享数据的代码的部分给包起来
*
* 注意:
* (1).同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
* (2).多个线程必须是同一把锁。
注:以下四种方法的测试类都一样(只有2.2写了测试类,其余省略了)
代码如下👇🏻
- package Sell;
-
- public class SellTicket implements Runnable {
-
- private int tickets = 20;//总票数
- private int ticketId = 1;//票号
- Object o =new Object();//全局锁
-
- @Override
- public void run() {
- while (tickets>0) {
- synchronized (o) {
- if(tickets>0) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
- tickets--; //剩余票数--
- ticketId++; //票号++
- }
- }
-
- }
- }
- }
- package Sell;
-
-
- public class SellTicketDemo1 {
- public static void main(String[] args) {
- // 创建资源对象
- SellTicket1 st = new SellTicket1();
-
- // 创建三个线程对象
- Thread t1 = new Thread(st, "窗口1");
- Thread t2 = new Thread(st, "窗口2");
- Thread t3 = new Thread(st, "窗口3");
-
- // 启动线程
- t1.start();
- t2.start();
- t3.start();
- }
- }
运行结果👇🏻
代码如下👇🏻
- package Sell;
-
- public class SellTicket1 implements Runnable {
-
- private int tickets = 20;//总票数
- private int ticketId = 1;//票号
- Demo lock =new Demo();//任意锁
-
- @Override
- public void run() {
- while (tickets>0) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized (lock) {
- // 为什么有if判断?
- // 如果没有if,t1执行结束,tickets=0,这样t2,t3再执行同步代码块之后,票会变成负数,票会多卖
- if(tickets>0) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
- tickets--; //剩余票数--
- ticketId++; //票号++ // 多卖21,22
- }
- }
-
- }
- }
- }
- class Demo{
- //任意类
- }
运行结果👇🏻
代码如下👇🏻
- package Sell;
-
- public class SellTicket1 implements Runnable {
-
- private int tickets = 20;//总票数
- private int ticketId = 1;//票号
-
- @Override
- public void run() {
- Object lock =new Object();//局部锁锁不住
- while (tickets>0) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized (lock) {
- // 为什么有if判断?
- // 如果没有if,t1执行结束,tickets=0,这样t2,t3再执行同步代码块之后,票会变成负数,票会多卖
- if(tickets>0) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
- tickets--; //剩余票数--
- ticketId++; //票号++ // 多卖21,22
- }
- }
-
- }
- }
- }
运行结果👇🏻
总结:该方法不可以,有重票和漏票问题。
代码如下👇🏻
- package Sell;
-
- public class SellTicket1 implements Runnable {
-
- private int tickets = 20;//总票数
- private int ticketId = 1;//票号
-
- @Override
- public void run() {
- while (tickets>0) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- synchronized (this) {
- // 为什么有if判断?
- // 如果没有if,t1执行结束,tickets=0,这样t2,t3再执行同步代码块之后,票会变成负数,票会多卖
- if(tickets>0) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
- tickets--; //剩余票数--
- ticketId++; //票号++ // 多卖21,22
- }
- }
-
- }
- }
- }
运行结果👇🏻
说明:项目开发中一般使用this关键字作为锁对象。
代码如下👇🏻
- package Sell;
-
- public class SellTicket1 implements Runnable {
-
- private int tickets = 20;//总票数
- private int ticketId = 1;//票号
-
- @Override
- public void run() {
- while (tickets>0) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- Selltickets ();
-
- }
- }
- //方法抽取:
- //同步方法
- public synchronized void Selltickets () {
- if(tickets>0) {
-
- System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
- tickets--; //剩余票数--
- ticketId++; //票号++ // 多卖21,22
- }
- }
- }
- package Sell;
-
-
- public class SellTicketDemo1 {
- public static void main(String[] args) {
- // 创建资源对象
- SellTicket1 st = new SellTicket1();
-
- // 创建三个线程对象
- Thread t1 = new Thread(st, "窗口1");
- Thread t2 = new Thread(st, "窗口2");
- Thread t3 = new Thread(st, "窗口3");
-
- // 启动线程
- t1.start();
- t2.start();
- t3.start();
- }
- }
运行结果👇🏻
(1).同步方法的格式及锁对象问题?
把同步关键字加在方法上
(2).同步代码块的锁对象是谁?
任意全局对象 一般使用this作为锁对象
(3).同步方法的锁是谁?
this,它是隐含的
Lock:
* void lock(): 获取锁。
* void unlock():释放锁。
* ReentrantLock是Lock的实现类.
代码如下👇🏻
- package Sell;
-
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
-
- public class SellTicket1 implements Runnable {
-
- private int tickets = 20;//总票数
- private int ticketId = 1;//票号
- Lock lock =new ReentrantLock();//同步锁(重入锁)
-
- @Override
- public void run() {
- while (tickets>0) {
- try {
- Thread.sleep(50);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- lock.lock();//上锁
- if(tickets>0) {
-
- System.out.println(Thread.currentThread().getName() + "正在出售第"+ ticketId + "张票");
- tickets--; //剩余票数--
- ticketId++; //票号++
- }
- lock.unlock();//开锁
- }
- }
- }
- package Sell;
-
-
- public class SellTicketDemo1 {
- public static void main(String[] args) {
- // 创建资源对象
- SellTicket1 st = new SellTicket1();
-
- // 创建三个线程对象
- Thread t1 = new Thread(st, "窗口1");
- Thread t2 = new Thread(st, "窗口2");
- Thread t3 = new Thread(st, "窗口3");
-
- // 启动线程
- t1.start();
- t2.start();
- t3.start();
- }
- }
运行结果👇🏻
🌺建议大家亲自动手操作,学编程,多实践练习是提升编程技能的必经之路。
🌺欢迎大家在评论区进行讨论和指正!