多线程_17_并发_同步_synchronized方法和synchronized块

用synchronized关键字目标一定要对。

上次模拟抢票的案例出现了负数问题,再在分析一下:假如就剩下一张票了(临界值),三个来抢票,假如B线程来了ticketNums=1,不少于等于0,然后延时200毫秒,此时还没有修改ticketNums的数据,还是等于1,然后A进来了,ticketNums的值等于1,也延时毫秒,然后C也进来了,延时200 毫秒,然后延时一过B就才把数据1拿走了,然后就没成了0,A也好了只能拿0,C就只能拿-1。
在这里插入图片描述
还有出现了数据相同的情况:每一线程都有自己的工作空间,它们和主存打交道的,假如主存是个10,A线程先把10拷贝过来,还没来的急修改,然后B线程也把这个10拷贝过来了,所以就出现了两10。
在这里插入图片描述
案例:同步方法

package com.cb.thread.day03;
/*
 * 线程安全:在并发时保证数据的正确性、效率尽可能高
 * synchronized
 * 1.同步方法
 * 2.同步块
 */



public class SynTest01 {

	public static void main(String[] args) {
		//一份资源
		SafeWeb12306 web = new SafeWeb12306();
		System.out.println(Thread.currentThread().getName());
		
		//三个代理 
		new Thread(web,"码畜").start(); //加上名字,区分线程
		new Thread(web,"码农").start();
		new Thread(web,"码蟥").start();
	}
}
class SafeWeb12306 implements Runnable{
	private int ticketNums = 10; //99张票
	private boolean flag = true;
	@Override
	public void run() {
		while(flag){
			test();
		}
		
	}
	public synchronized void test(){
		if(ticketNums<=0){
			flag= false;
			return;
		}
		try {
			Thread.sleep(200);//模拟延时
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//Thread.currentThread().getName()谁运行run就是代表谁
		System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);

	}
	
}

运行结果:
在这里插入图片描述
上次还有个案例,账户里面有100万,两人同时取钱,最后账户里面的钱出现了负数,什么原因了,当A取80万,然后在判断账户里面的钱是否有80万,然后判断够取80万,然后正取的时候,B取90万也判断,注意这时数据还来的急修改还是100万,一判断也够也进来了,这时A取出了80万还剩20万,然后B也取了90万,变成了-70万
在这里插入图片描述
在这里插入图片描述
接下来我们来改一下上次的代码

package com.cb.thread.day03;

public class SynTest02 {
	public static void main(String[] args) {
		//账户
		Account account = new Account(100, "结婚礼金");
		SafeDrawing you = new SafeDrawing(account, 70, "男方");
		SafeDrawing wife = new SafeDrawing(account, 80, "女方");
		you.start();
		wife.start();
	}
}
class SafeDrawing extends Thread{
	Account account; //取钱的账户
	int drawingMoney;//取的钱数
	int packetTotal;//口袋里面的钱
	
	
	public SafeDrawing(Account account, int drawingMoney, String name) {
		super(name);
		this.account = account;
		this.drawingMoney = drawingMoney;
	}


	@Override
	public void run() {
		test();
	}
	//目标锁定account
	public  void test(){
		//提高性能,如果账户没钱还来访问等待性能很低。
		if (account.money<=0) {
			return;
		}
		//同步块
		synchronized(account){
		
		if (account.money-drawingMoney<0) {
			return;
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		account.money -=drawingMoney;
		packetTotal +=drawingMoney;
		System.out.println(this.getName()+"-->"+"账户余额为"+account.money);
		System.out.println(this.getName()+"口袋里面的钱-->"+packetTotal);
		}
	}
	
}
class  Account{
	 int money;//金额
	 String name; //名称
	public Account(int money,String name) {
		this.money = money;
		this.name = name; 
	}
}

运行结果:
在这里插入图片描述
从结果上分析只有一个人成功的取到了钱,另外一人没有取到钱,因为账户里面的钱不够了。还有就是如果是锁方法就是this,在这里要锁的是账户,所以就要用到方法块,当这个A先进去了,先账户里面的钱够不够取,如果够了话,就进去取,这时B进去了访问锁,必须等A把钱取出来,B才能进去取钱。

上次还有一个容器数据不正确,往里面存10000,结果不够数,下面就来改正它

package com.cb.thread.day03;

import java.util.ArrayList;
import java.util.List;

public class SynTest03 implements Runnable{
	public static List<String> list = new ArrayList<String>();

	 
	public static void main(String[] args) throws InterruptedException {
		
		for (int i = 0; i < 10000; i++) {
			
				SynTest03 u03 =new SynTest03();
				Thread t = new Thread(u03);
				t.start();
			
			
		}
		Thread.sleep(10000);
		System.out.println(list.size());
	}

	@Override
	public  void  run() {
		synchronized(list){
			list.add(Thread.currentThread().getName());
		}
	}
}

运行结果:每存进去一个,出来后在存进去一下个,就不会出现覆盖的情况
在这里插入图片描述
将第一个案例用同步块

package com.cb.thread.day03;

public class SynTest04 {

	public static void main(String[] args) {
		//一份资源
		SafeWeb123061 web = new SafeWeb123061();
		System.out.println(Thread.currentThread().getName());
		
		//三个代理 
		new Thread(web,"码畜").start(); //加上名字,区分线程
		new Thread(web,"码农").start();
		new Thread(web,"码蟥").start();
	}

}
class SafeWeb123061 implements Runnable{
	private int ticketNums = 10; //99张票
	private boolean flag = true;
	@Override
	public void run() {
		while(flag){
			
			test5();
		}
		
	}
	//线程同步方法
	public synchronized void test1(){
		if(ticketNums<=0){
			flag= false;
			return;
		}
		try {
			Thread.sleep(200);//模拟延时
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//Thread.currentThread().getName()谁运行run就是代表谁
		System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);

	}
	//同步块 范围太大 ,性能低下
	public  void test2(){
		synchronized(this){
			if(ticketNums<=0){
				flag= false;
				return;
			}
			try {
				Thread.sleep(200);//模拟延时
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//Thread.currentThread().getName()谁运行run就是代表谁
			System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
		}

	}
	//线程不安全  ticketNums对象在变,要锁一个地址不变的
	public  void test3(){
		synchronized((Integer)ticketNums){
			if(ticketNums<=0){
				flag= false;
				return;
			}
			try {
				Thread.sleep(200);//模拟延时
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//Thread.currentThread().getName()谁运行run就是代表谁
			System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
		}

	}
	// 线程不安全范围太小锁不住
	public  void test4(){
		synchronized(this){
			if(ticketNums<=0){
				flag= false;
				return;
			}
		}	
			try {
				Thread.sleep(200);//模拟延时
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			//Thread.currentThread().getName()谁运行run就是代表谁
			System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
		}
	// 
		public  void test5(){
			if(ticketNums<=0){ //考虑的是没有票的情况
				flag= false;
				return;
			}
			synchronized(this){	
				if(ticketNums<=0){ //考虑最后的1张票
					flag= false;
					return;
				}
			
				try {
					Thread.sleep(200);//模拟延时
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//Thread.currentThread().getName()谁运行run就是代表谁
				System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
			
			}
		}
	
	
}

分析test5:如果不在一进去的时候判断的话,那么当没有票了,B进去了操作,然后其他线程还要等B线程出来,然后再进去,其他线程也一样在再等着,然后已经票还要进行这一系列操作。如果加了话,没有票就直接可以出来。这叫多重检查,后面多用块,少用方法,这样性能高。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值