以卖票的例子来介绍多线程和资源共享,下面我们来看看为什么要用卖票作为例子。
卖票是包含一系列动作的过程,有各种操作,例如查询票、收钱、数钱、出票等,其中有一个操作是每次卖掉一张,就将总的票数减去1。有10张票,如果一个人卖票,先做查票、收钱、数钱等各种操作,再将总的票数减去1,效率很低。如果多个人卖票,每个人都是做同样的操作,数钱、检查钱,最后将总的票数减1,这样效率高。但是有一个问题,如果出现两个人同时将总的票数减掉了1,例如,A、B两个人同时读取到票的总数是10,A从中减去1,同时B也从中减去1,总数显示是9,其实票只有8张。导致数据错误。
按照正常逻辑,同一时刻只允许一个人来从总票数中减去1,A读取总票数,再减去1的过程中,B必须等待,等A操作完了,B才能进行。其实票就是共享资源,一次只能由一个人访问。这里就要用到同步机制,即锁机制,使用关键词synchronized将读取总的票数,并减去1的操作锁定,使得一次只能由一个人访问。每个售票员就是一个线程,多个售票员进行同一项卖票任务。
synchronized原理是,执行synchronized部分代码的时候必须需要对象锁,而一个对象只有一个锁,只有执行完synchronized里面的代码后释放锁,其他线程才可以获得锁,那么就保证了同一时刻只有一个线程访问synchronized里面的代码。使得资源共享的关键是,只有一个实例,synchronized使用的是同一把锁,用实例的锁或者定义一个实例。这就需要使用实现Runnable接口的方式,实现多线程,这样传入的是一个实例。继承Thread的方式,传入的是多个实例,每个实例都有一个锁,那就无法实现控制。
具体代码如下:
package com.test; public class SaleTickets implements Runnable { private int ticketCount = 10;// 总的票数,这个是共享资源,多个线程都会访问 Object mutex = new Object();// 锁,自己定义的,或者使用实例的锁 /** * 卖票 */ public void sellTicket() { synchronized (mutex)// 当操作的是共享数据时, // 用同步代码块进行包围起来,执行里面的代码需要mutex的锁,但是mutex只有一个锁。这样在执行时,只能有一个线程执行同步代码块里面的内容 { if (ticketCount > 0) { ticketCount--; System.out.println(Thread.currentThread().getName() + "正在卖票,还剩" + ticketCount + "张票"); } else { System.out.println("票已经卖完!"); return; } } } public void run() { while (ticketCount > 0)// 循环是指线程不停的去卖票 { sellTicket(); /** * 在同步代码块里面睡觉,和不睡效果是一样 的,作用只是自已不执行,也不让线程执行。sleep不释放锁,抱着锁睡觉。其他线程拿不到锁,也不能执行同步代码。wait()可以释放锁 * 所以把睡觉放到同步代码块的外面,这样卖完一张票就睡一会,让其他线程再卖,这样所有的线程都可以卖票 */ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
下面是调用:
package com.test; public class TicketMain { public static void main(String[] args) { SaleTickets runTicekt = new SaleTickets();//只定义了一个实例,这就只有一个Object mutex = new Object();即一个锁。 Thread th1 = new Thread(runTicekt, "窗口1");//每个线程等其他线程释放该锁后,才能执行 Thread th2 = new Thread(runTicekt, "窗口2"); Thread th3 = new Thread(runTicekt, "窗口3"); Thread th4 = new Thread(runTicekt, "窗口4"); th1.start(); th2.start(); th3.start(); th4.start(); } }
输出:
窗口1正在卖票,还剩9张票 窗口4正在卖票,还剩8张票 窗口3正在卖票,还剩7张票 窗口2正在卖票,还剩6张票 窗口3正在卖票,还剩5张票 窗口2正在卖票,还剩4张票 窗口1正在卖票,还剩3张票 窗口4正在卖票,还剩2张票 窗口3正在卖票,还剩1张票 窗口1正在卖票,还剩0张票 票已经卖完!
这是多个线程,完成同一个任务的情况,即多个线程调用同一个实例,通过实现Runable接口实现。多个线程可以异步的做这个任务中其他事情,但是对于共享资源的访问只能以同步的方式操作,即一个接一个访问共享资源,其他资源可以并行访问。
另一种实现多线程的方式是继承Thread,调用的时候需要传递多个实例,这是多个线程,多个实例的情况,每个线程独立处理一个实例,各个线程不能实现资源共享。
忍者必须死34399账号登录版 最新版v1.0.138v2.0.72
下载勇者秘境oppo版 安卓版v1.0.5
下载忍者必须死3一加版 最新版v1.0.138v2.0.72
下载绝世仙王官方正版 最新安卓版v1.0.49
下载Goat Simulator 3手机版 安卓版v1.0.8.2
Goat Simulator 3手机版是一个非常有趣的模拟游
Goat Simulator 3国际服 安卓版v1.0.8.2
Goat Simulator 3国际版是一个非常有趣的山羊模
烟花燃放模拟器中文版 2025最新版v1.0
烟花燃放模拟器是款仿真的烟花绽放模拟器类型单机小游戏,全方位
我的世界动漫世界 手机版v友y整合
我的世界动漫世界模组整合包是一款加入了动漫元素的素材整合包,
我的世界贝爷生存整合包 最新版v隔壁老王
我的世界MITE贝爷生存整合包是一款根据原版MC制作的魔改整