浅谈线程安全

本文探讨了Java中的线程安全概念,通过模拟购票场景展示了线程不安全问题,并介绍了Java内存模型(JMM)的角色。文章进一步讲解了解决线程安全的策略,包括synchronized关键字、同步代码块以及JDK 1.5的Lock接口,通过实例分析了这些机制如何确保线程之间的同步交互,防止数据竞争问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

浅谈线程安全

1.什么是线程安全?

多个线程同一时刻对同一个全局变量(同一份资源)做写操作(读操作不会涉及线程安全)时,如果跟我们预期的结果一样,我们就称之为线程安全,反之,线程不安全。

2.举个栗子

同学们在过年时候买火车票、 那家伙、那场面那是相当大呀!那真是:锣鼓喧天,鞭炮齐鸣,红旗招展,人山人海呀。 我们下面用代码模拟一下业务场景。

package com.example.snailthink;

/**
 * @program: snailthink
 * @description:
 * @author: SnailThink
 * @create: 2021-09-03 14:19
 **/
public class MyThread implements Runnable {


	private int ticketCount=50;

	@Override
	public void run() {

		try {
			while (ticketCount<50){
				Thread.sleep(50);
			}
		}catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+":  抢到第"+ticketCount--+"张");
	}
}

测试类

package com.example.snailthink;

/**
 * @program: snailthink
 * @description:
 * @author: SnailThink
 * @create: 2021-09-03 14:09
 **/
public class MyThreadTests{

	private  int count=50;

	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		Thread thread = new Thread(myThread,"小李");
		Thread thread1 = new Thread(myThread,"小张");
		Thread thread2 = new Thread(myThread,"小王");
		thread.start();
		thread1.start();
		thread2.start();
	}

}

运行结果

通过上面的测试结果,三个线程,同时抢票,有时候会抢到同一张票?为什么会有这种问题发现?

在回答这个问题之前,我们应该了解一下 Java 的内存模型(JMM)

3.什么是JMM?

**JMM(Java Memory Model),**是一种基于计算机内存模型,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。保证共享内存的原子性、可见性、有序性

可见性: 多线程操作共享内存时,执行结果能够及时的同步到共享内存,确保其他线程对此结果及时可见。

我们通过图,把之前抢票的业务画出来。

同一进程下的多个线程,内存资源是共享的。主内存的count才是共享资源。 小李、小张、小王 实际上不是直接对主内存的count进行写入操作 程序运行过程中,他们每个人,都有各自的工作内存。实际上就是把主内存的count,每个人,都copy一份,对各自的工作内存的变量进行操作。操作完后,再把对应的结果通知到主内存。

模拟抢票demo为什么会有线程安全问题的原因就是:

因为各自都操作自己的工作内存,拿到主内存的值就开始操作。假设,这时候count为23,同一时间,来了俩个线程,那这俩个线程的工作内存拿到的值都是23,这样就会导致,这俩个线程,都会抢到23这张票。

4. 怎么解决线程安全问题?

要想解决线程安全的问题,我们就需要解决一个问题,就是线程之间进行同步交互。了解可见性后,我们知道是没有办法相互操作对方的工作内存的。
一般有如下几种方法

  • synchronized关键字(放在方法上)
  • 同步代码块
  • jdk1.5的Lock
4.1 synchronized关键字(放在方法上)
package com.example.snailthink.test.thread;

/**
 * @program: snailthink
 * @description:
 * @author: SnailThink
 * @create: 2021-09-03 14:43
 **/
public class MySynchronizedThread implements Runnable {
	private  int count=50;

	@Override
	public  void run() {
		while(true){
			buy();
		}

	}
	public synchronized  void  buy(){
		if(count>0){
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+":  抢到第"+count--+"张,"+System.currentTimeMillis());
		}
	}
}

测试结果

通过图片我们可以发现,同一时间,抢票的间隔差不多都是50ms . 为什么多线程会失效???

因为在抢票的方法上,增加了synchronized,导致同一时候,只能有一个线程运行,需要等这个线程运行完后,下一个线程才能运行。

4.2 同步代码块
  • 这种方式就是利用synchronized+锁对象

  • 相对于synchronized 放在方法上方式,性能有提升,只锁了代码块,而不是把这个方法都锁咯。

package com.example.snailthink.test.thread;

/**
 * @program: snailthink
 * @description:
 * @author: SnailThink
 * @create: 2021-09-03 14:41
 **/
public class SynchronizedBlockThreadTest implements Runnable {
	private int count = 50;
	private Object object = new Object();

	@Override
	public void run() {
		while (true) {
			buy();
		}

	}

	public void buy() {
		synchronized (object) {
			if (count > 0) {
				try {
					Thread.sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + ":  抢到第" + count-- + "张," + System.currentTimeMillis());
			}
		}
	}
}
4.3 jdk1.5的Lock
package com.example.snailthink.test.thread;

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

/**
 * @program: snailthink
 * @description:
 * @author: SnailThink
 * @create: 2021-09-03 14:40
 **/
public class LockThreadTests implements Runnable {
	private int count = 50;
	//定义锁对象
	private Lock lock = new ReentrantLock();

	@Override
	public void run() {
		while (true) {
			buy();
		}

	}

	public void buy() {
		lock.lock();
		if (count > 0) {
			try {
				Thread.sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + ":  抢到第" + count-- + "张," + System.currentTimeMillis());
		}
		lock.unlock();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值