35.认识线程安全【20220813】

先看看这个卖票实例:

package com.wangxing.test6;
public class MyThread1 implements Runnable{
	private  int  piao=5;
	@Override
	public void run() {
		//得到线程名称
		String name=Thread.currentThread().getName();
		//持续买票
		boolean flag=true;
		while(flag){
			//判断有没有可卖的票
			if(piao>0){
				//收钱--找钱--打印票
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(name+",卖出1张票,还剩"+(--piao)+"张");
			}else{
				flag=false;
			}
		}
	}
}

package com.wangxing.test6;
public class Main1 {
	public static void main(String[] args) {
		MyThread1  my=new MyThread1();
		Thread  th1=new Thread(my);
		Thread  th2=new Thread(my);
		Thread  th3=new Thread(my);
		th1.setName("窗口1");
		th2.setName("窗口2");
		th3.setName("窗口3");
		th1.start();
		th2.start();
		th3.start();
	}
}

运行结果:
窗口3,卖出1张票,还剩3张
窗口1,卖出1张票,还剩4张
窗口2,卖出1张票,还剩2张
窗口2,卖出1张票,还剩1张
窗口1,卖出1张票,还剩0张
窗口3,卖出1张票,还剩-1张

分析结果:当窗口1卖最后一张票的时候,在收钱打印票的时候,还没有来得及对票数进行减1之前,线程就切换给了窗口3,窗口3认为还有一张票,窗口3就收钱收钱打印票的时候,还没有来得及对票数进行减1之前,线程有切换给了窗口1,窗口1就对票数进减1,完成以后线程切换给窗口3,窗口3对对票数进行减1此时就得到-1这个值。

经过上面运行程序的分析,我得到的结果是:当多条线程,同时访问同一个资源的时候,会产生数据不一致的错误情况。

为了解决这种数据不一致的错误情况,我们才学习线程同步。

什么是线程同步/线程安全?

线程同步也叫线程安全,当多条线程,同时访问同一个资源的时候,每一次只能由多条线程中的其中一条访问公共资源,当这一条线程访问公共资源的时候,其他的线程都处于等待状态,不能访问公共资源,当这一条线程访问完了公共资源以后,其他线程中的一条线程才能访问资源,剩下的线程继续等待,等待当前线程访问结束,实现这个过程就是线程同步。【排队访问资源】

线程同步/线程安全的实现方式

1.Synchronized关键字 【同步代码块/同步方法】
   1.1.同步代码块
         格式:synchronized(同步对象){

         }

package com.wangxing.test6;
public class MyThread2 implements Runnable {
	private int piao = 5;
	@Override
	public void run() {
		// 得到线程名称
		String name = Thread.currentThread().getName();
		// 持续买票
		boolean flag = true;
		while (flag) {
			// 同步代码块
			synchronized (this) {
				// 判断有没有可卖的票
				if (piao > 0) {
					// 收钱--找钱--打印票
					try {
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(name + ",卖出1张票,还剩" + (--piao) + "张");
				} else {
					flag = false;
				}
			}
		}
	}
}

同步代码块虽然可以实现买票的效果,但是它在使用的时候,需要设置一个同步对象,由于我们很多时候都不知道这个同步对象应该是谁,容易写错,造成死锁的情况。正是应为这个缺点,我们很少使用同步代码块来实现线程同步。

 1.2.同步方法
       同步方法的定义格式: 访问限制修饰符  synchronized  方法返回值类型 方法名称(){}

package com.wangxing.test6;

public class MyThread3 implements Runnable{
	private  int  piao=5;
	//持续买票
	boolean flag=true;
	@Override
	public void run() {
		//得到线程名称
		String name=Thread.currentThread().getName();
		while(flag){
			sellpiao(name);
		}
	}
	//同步方法
	public  synchronized   void sellpiao(String  name){
		//判断有没有可卖的票
		if(piao>0){
			//收钱--找钱--打印票
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(name+",卖出1张票,还剩"+(--piao)+"张");
		}else{
			flag=false;
		}
	}
}

2.通过Lock接口
   public interface Lock
   常用的接口方法
   void    lock() 获得锁。 
   void    unlock() 释放锁。
   由于上面的锁方法是Lock接口,我们要使用就得先创建出Lock接口对象,由于Lock是个接口不能new ,我们就得使用它的子类来创建对象。
   Lock接口得子类ReentrantLock

package com.wangxing.test6;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyThread4 implements Runnable{
	private  int  piao=5;
	//定义Lock对象
	private  Lock  mylock=new ReentrantLock();
	@Override
	public void run() {
		//得到线程名称
		String name=Thread.currentThread().getName();
		//持续买票
		boolean flag=true;
		while(flag){
			//void	lock() 获得锁。 
			mylock.lock();
			//判断有没有可卖的票
			if(piao>0){
				//收钱--找钱--打印票
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(name+",卖出1张票,还剩"+(--piao)+"张");
			}else{
				flag=false;
			}
			//void	unlock() 释放锁。
			mylock.unlock();
		}
	}
}

Synchronized关键字与Lock接口的区别?
synchronized:
    1.synchronized关键字
    2.自动锁定资源,不灵活
    3.异常时会自动释放锁
    4.不能中断锁,必须等待线程执行完成释放锁。
Lock:
    1.Lock接口
    2.手动锁定资源,灵活
    3.异常时不会自动释放锁,所以需要在finally中实现释放锁
    4.可以中断锁

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值