Java 三个线程顺序 100_Java中关于解决多线程创建和安全问题的几种方式,通过模拟3个售票窗口卖出100张票的情节...

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有

执行完,另一个线程参与进来执行。导致共享数据的错误。此时,多线程的安全问题就会出现。

解决办法是对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以

参与执行。

通过模拟3个售票窗口卖出100张票的情节,用代码的方式来实现。

一、多线程的创建

多线程的创建有4中方式,在jdk5之前有两种,第1种是继承Thread类的方法,第2种是实现Runnable接口

jdk5之后,也有2种,一种是实现Callable接口,另一种是使用线程池的方式。

核心都是覆盖Thread类中的run(),多个线程执行相同或不同的run()。

class Window implements Runnable {

private int ticket = 100;

@Override

public void run() {

while (true) {

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

if (ticket > 0) {

System.out.println(Thread.currentThread().getName() + "票号:" + ticket);

ticket--;

} else {

break;

}

}

}

}

public class WindowTest {

public static void main(String[] args) {

Window t = new Window();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

Thread t3 = new Thread(t);

t1.setName("售票窗口1:");

t2.setName("售票窗口2:");

t3.setName("售票窗口3:");

t1.start();

t2.start();

t3.start();

}

}

运行结果如下

560e68b86fddc426da5ed3d177d48cb7.png

上面很明显出现了不同窗口卖了同一张票的问题,有些票号没有出现。这是很明显的线程安全问题。

二、同步代码块

class Window implements Runnable {

private int ticket = 100;

@Override

public void run() {

while (true) {

synchronized (this) {

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

if (ticket > 0) {

System.out.println(Thread.currentThread().getName() + "票号:" + ticket);

ticket--;

} else {

break;

}

}

}

}

}

public class WindowTest {

public static void main(String[] args) {

Window t = new Window();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

Thread t3 = new Thread(t);

t1.setName("售票窗口1:");

t2.setName("售票窗口2:");

t3.setName("售票窗口3:");

t1.start();

t2.start();

t3.start();

}

}

568efefd2981fb6ff9d6134491a8d398.png

很明显,通过synchronized的使用可以让不同窗口卖出不同的一张票,所有的票也都得到了解决。

三、同步方法

class Window implements Runnable {

private int ticket = 100;

@Override

public void run() {

while (true){

if (ticket>0){

show();

}else {

break;

}

}

}

public synchronized void show(){

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

if (ticket > 0) {

System.out.println(Thread.currentThread().getName() + "票号:" + ticket);

ticket--;

}

}

}

public class WindowTest {

public static void main(String[] args) {

Window t = new Window();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

Thread t3 = new Thread(t);

t1.setName("售票窗口1:");

t2.setName("售票窗口2:");

t3.setName("售票窗口3:");

t1.start();

t2.start();

t3.start();

}

}

也是可以解决上面的问题

帮助文档

快捷键目录标题文本样式列表链接代码片表格注脚注释自定义列表LaTeX 数学公式插入甘特图插入UML图插入Mermaid流程图插入Flowchart流程图插入类图

标题复制

四、Lock

从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同

步锁对象来实现同步。同步锁使用Lock对象充当。

class Lock implements Runnable {

private int ticket=100;

private final ReentrantLock reentrantLock = new ReentrantLock();

@Override

public void run() {

while (true){

try {

//调用锁定方法lock()

reentrantLock.lock();

if (ticket>0){

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+"票号:"+ticket);

ticket--;

}else {

break;

}

}finally {

reentrantLock.unlock();

}

}

}

}

public class LockTest {

public static void main(String[] args) {

Lock t = new Lock();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

Thread t3 = new Thread(t);

t1.setName("售票窗口1:");

t2.setName("售票窗口2:");

t3.setName("售票窗口3:");

t1.start();

t2.start();

t3.start();

}

}

ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和

内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以

显式加锁、释放锁。

五、总结

Lock只有代码块锁,synchronized有代码块锁和方法锁

使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)

Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放

优先使用顺序:

Lock >同步代码块(已经进入了方法体,分配了相应资源)> 同步方法(在方法体之外)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值