线程学习(二)--线程安全问题

线程安全问题出现的情况

  • 多线程
  • 共享实例变量
  • 至少有一个或一个以上的线程读写变量

当一个程序中有多个线程操作同一个共享变量的时候我们称它为非线性安全的
以买票为例:如果有两个人同时买一张票那买票环节就会出现问题
这个是为什么呢?
让我们来看看线程安全问题产生的原因

线程安全问题所产生的原因

首先我们来看看Java的内存模型
请添加图片描述

  • 由图中我们可以看出java中的线程操作一个变量有三步:首先取出主内存中的共享变量保存在线程的本地内存中,然后线程对共享变量的副本进行操作,最后将共享变量的副本更新为主内存中的共享变量。由此我们可以看出线程并不是直接对内存的变量进行操作的。
  • 以i++为例,线程先将i取出来,然后将i+1,最后将i+1赋值给内存中的i。
  • 如果只有一个线程,那么将不会出现安全问题,但是当有多个线程的时候某一个线程在取用某一个变量,还未来得及更新变量的同时,另外一个线程取用操作这个变量了,此时便会导致出现线性安全问题

以买票代码为例子:

//设置票类
public class Ticket {
	//默认100张票
	public int number=100;

}
//创建线程买票
public class User implements Runnable{
	
	private Ticket ticket;
	private Lock lock=new ReentrantLock();
	public User(Ticket ticket) {
		this.ticket=ticket;
	}
	
	public void run() {
		while(ticket.number>1) {
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		synchronized (ticket) {
			ticket.number--;
			System.out.println(Thread.currentThread().getName()+"买了第:"+ticket.number+"张票");

		


	 }
	}

}
public class BuyTicket {
	
	public static void main(String args[]) {
		Ticket ticket=new Ticket();
		User user=new User(ticket);
		User user2=new User(ticket);
		
		new Thread(user).start();
		new Thread(user2).start();
	}

}
//

此时运行便会出现两个线程买同一张票的情况
请添加图片描述
那我们如果去解决它呢?

解决办法

同步机制

  • 同步机制也就是给我们所运行的线程加一个锁,只有取到锁的线程才可以访问该共享数据,其他线程必须等到该线程执行完毕释放锁之后才能获取锁访问数据;
  • 实现该机制我们有一下方法:同步代码块、同步方法、Lock手动上锁。

同步代码块

	//同步代码块使用格式
	synchronized(对象){
	}


	//买票实例
	//1、同步代码块
	synchronized (ticket) {
			ticket.number--;
			System.out.println(Thread.currentThread().getName()+"买了第:"+ticket.number+"张票");
		}
  • 括号里的对象也就是我们要取出的锁(监视器)的对象,当两个并发的线程访问同一个对象时,持有锁的线程才能运行。
  • 所以使用同步代码前提是:
    1、两个或两个以上的并发线程
    2、这几个线程需要指定同一个监听器也就是需要取出同一个对象的锁
  • 使用同步代码块它的好处是保证了线程的安全性但是缺点便是较为占用资源

同步方法

使用格式


权限修饰符 synchronized 返回值类型 方法名(){
	//需要被同步的代码
}

静态同步方法:方法上加上static关键字,使用synchronized 关键字修饰 或者使用类.class文件。
静态的同步函数使用的锁是该函数所属字节码文件对象;

Lock手动上锁

		//创建锁对象
			private Lock lock=new ReentrantLock();


		//在线程中
		//手动上锁
		try {
			
			//上锁
			lock.lock();
			ticket.number--;
			System.out.println(Thread.currentThread().getName()+"买了第:"+ticket.number+"张票");
		}
		//为了防止出现死锁用finally
		finally {
			//释放锁
			lock.unlock();
			
		}
  • 注意:这里取锁的对象是这个线程对象,所以在主线程中只能创建一个线程对象
public class BuyTicket {
	
	public static void main(String args[]) {
		Ticket ticket=new Ticket();
		User user=new User(ticket);
		
		new Thread(user).start();
		new Thread(user).start();
	}

}

为了防止程序出现错误没来得及释放锁设置finally来执行释放锁。因为finally里的语句最后都会执行。

设置了同步之后两个人买一张票的情况就没有出现了~
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值