Java线程同步

线程同步:要在多线程的环境下保证数据的准确性和安全性

线程不安全的示例1:银行取钱

多个人同时取钱时,账户余额会出现负数的情况

/**
 * 線程不安全:取錢
 * @author Jack
 *
 */
public class DrawMoney {
	public static void main(String[] args) {
		//賬號
		Account account = new Account(100, "結婚禮金");
		
		Drawing you = new Drawing(account, 80, "可悲的你");
		Drawing wife = new Drawing(account, 90, "happy的她");
		you.start();
		wife.start();
	}
}

/**
 * 賬戶
 */
class Account{
	int money;//金額
	String name;//名稱
	public Account(int money, String name) {
		this.money = money;
		this.name = name;
	}
}

/**
 * 模擬取款
 */
class Drawing extends Thread{
	Account account;//取錢的賬戶
	int drawingMoney;//取的錢數
	int packetTotal;//口袋的總數
	public Drawing(Account account, int drawingMoney, String name) {
		super(name);
		this.account = account;
		this.drawingMoney = drawingMoney;
	}
	@Override
	public void run() {
		account.money-=drawingMoney;
		packetTotal+=drawingMoney;
		System.out.println(this.getName()+"-->賬戶餘額爲:"+account.money);
		System.out.println(this.getName()+"-->口袋的錢爲:"+packetTotal);
	}
}

在这里插入图片描述

线程不安全示例2:操作容器

启动一万个线程,并把每个线程的名称添加到容器中,结果容器的大小并不是10000

import java.util.ArrayList;
import java.util.List;
/**
 * 線程不安全:操作容器
 * @author Jack
 *
 */
public class ListTest {
	public static void main(String[] args) {
		final List<String> list = new ArrayList<String>();
		for(int i=0; i<10000; i++){
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					list.add(Thread.currentThread().getName());
				}
			}).start();
		}
		System.out.println(list.size());
	}
}

在这里插入图片描述

并发:同一个对象被多个线程同时操作在这里插入图片描述

并发的情况很可能造成数据的不准确,为了解决这个问题需要线程同步机制

线程同步:队列+锁 (synchronized)

在这里插入图片描述
同步之后会导致性能下降
在这里插入图片描述

synchronized方法

synchronized快

注意:synchronized锁的范围太大会影响性能,范围太小的话则可能锁不住导致数据不安全。要在保证安全的情况下兼顾性能,这是个难点

解决多人取款的线程同步问题:

/**
 * 線程同步:取錢
 * 同步塊:目標更明確
 * @author Jack
 *
 */
public class DrawMoney {
	public static void main(String[] args) {
		//賬號
		Account account = new Account(100, "結婚禮金");
		
		Drawing you = new Drawing(account, 80, "可悲的你");
		Drawing wife = new Drawing(account, 90, "happy的她");
		you.start();
		wife.start();
	}
}

/**
 * 賬戶
 */
class Account{
	int money;//金額
	String name;//名稱
	public Account(int money, String name) {
		this.money = money;
		this.name = name;
	}
}

/**
 * 模擬取款
 */
class Drawing extends Thread{
	Account account;//取錢的賬戶
	int drawingMoney;//取的錢數
	int packetTotal;//口袋的總數
	public Drawing(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) {
				e.printStackTrace();
			}
			account.money-=drawingMoney;
			packetTotal+=drawingMoney;
			System.out.println(this.getName()+"-->賬戶餘額爲:"+account.money);
			System.out.println(this.getName()+"-->口袋的錢爲:"+packetTotal);
		}
	}
}

在这里插入图片描述

解决操作容器的线程同步问题:

import java.util.ArrayList;
import java.util.List;
/**
 * 線程同步:操作容器
 * @author Jack
 *
 */
public class ListTest {
	public static void main(String[] args) throws InterruptedException {
		final List<String> list = new ArrayList<String>();
		for(int i=0; i<10000; i++){
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					synchronized(list){
						list.add(Thread.currentThread().getName());
					}
				}
			}).start();
		}
		Thread.sleep(5000);
		System.out.println(list.size());
	}
}

在这里插入图片描述

多线程并发例子:电影院购票

支持多人同时购票,每个人可以选择坐位,通过synchronized代码块保证线程并发时数据的准确性

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

public class HappyCinema {
	public static void main(String[] args) {
		//电影院剩余位置
		List<Integer> availableSeats = new ArrayList<Integer>();
		availableSeats.add(1);
		availableSeats.add(3);
		availableSeats.add(5);
		availableSeats.add(7);
		availableSeats.add(8);
		
		//顾客1要买的位置
		List<Integer> seats1 = new ArrayList<Integer>();
		seats1.add(1);
		seats1.add(3);
		
		//顾客2要买的位置
		List<Integer> seats2 = new ArrayList<Integer>();
		seats2.add(1);
		seats2.add(8);
		
		Cinema c = new Cinema("happy cinema",availableSeats);
		new Thread(new Customer(c,seats1),"张三").start();
		new Thread(new Customer(c,seats2),"李四").start();
	}
}

/*
 * 顾客线程
 * 成员变量:影院(在哪个影院买)、要买的票数
 * 线程体中执行影院的购票方法
 */
class Customer implements Runnable{
	Cinema cinema;
	List<Integer> seats;

	public Customer(Cinema cinema, List<Integer> seats) {
		this.cinema = cinema;
		this.seats = seats;
	}

	@Override
	public void run() {
		synchronized (cinema) {
			boolean flag = cinema.bookTickets(seats);
			if(flag){
				System.out.println("出票成功,"+Thread.currentThread().getName()+"-->位置为:"+seats);
			}else{
				System.out.println("出票失败,"+Thread.currentThread().getName()+"-->位置不够");
			}
		}
	}
	
}

/*
 * 影院
 * 成員變量:影院名稱、剩余票数
 * 方法:购票(购买的票数<可用剩余票数,则购买成功,并且更新剩余票数。否则购票失败)
 */
class Cinema{
	String name;
	List<Integer> availableSeats;
	
	public Cinema(String name, List<Integer> availableSeats) {
		this.name = name;
		this.availableSeats = availableSeats;
	}
	//购票方法,传入一个参数:购票数量
	public boolean bookTickets(List<Integer> seats){
		System.out.println("欢迎光临"+this.name+",当前剩余票数为:"+availableSeats);
		List<Integer> copy = new ArrayList<Integer>();
		copy.addAll(availableSeats);
		//相减
		copy.removeAll(seats);
		//判断大小
		if(availableSeats.size()-copy.size()!=seats.size()){
			return false;
		}
		//成功
		availableSeats = copy;
		return true;
	}
}

在这里插入图片描述

多线程并发例子二:12306上购票(单张)


public class Happy12306 {
	public static void main(String[] args) {
		Web12306 c = new Web12306("12306官网", 5);
		new Passenger(c,"张三",4).start();
		new Passenger(c,"李四",2).start();
		
	}
}

/*
 * 乘客线程
 */
class Passenger extends Thread{
	int seats;

	public Passenger(Runnable target, String name, int seats) {
		super(target, name);
		this.seats=seats;
	}
}

/*
 * 火车票网
 */
class Web12306 implements Runnable{
	String name;
	int availableSeats;
	
	public Web12306(String name, int availableSeats) {
		this.name = name;
		this.availableSeats = availableSeats;
	}
	
	@Override
	public void run() {
		Passenger p = (Passenger)Thread.currentThread();
		boolean flag = this.bookTickets(p.seats);
		if(flag){
			System.out.println("出票成功,"+Thread.currentThread().getName()+"-->位置为:"+p.seats);
		}else{
			System.out.println("出票失败,"+Thread.currentThread().getName()+"-->位置不够");
		}
	}
	
	//购票方法,传入一个参数:购票数量
	public synchronized boolean bookTickets(int seats){
		System.out.println("欢迎光临"+this.name+",当前剩余票数为:"+availableSeats);
		if(seats>availableSeats){
			return false;
		}
		availableSeats-=seats;
		return true;
	}
}

在这里插入图片描述

并发容器:java.util.concurrent.CopyOnWriteArrayList 底层已经实现了synchronized,所以不需要再加同步锁

import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 并发容器
 * @author Jack
 *
 */
public class SynContainer {
	public static void main(String[] args) throws InterruptedException {
		final CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();
		for(int i=0;i<10000;i++){
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					list.add(Thread.currentThread().getName());
				}
			}).start();
		}
		Thread.sleep(10000);
		System.out.println(list.size());
	}
}

死锁:

在这里插入图片描述

/**
 * 死锁:过多的同步可能造成相互不释放资源,从而相互等待,一般发生于同步中持有多个对象的锁
 * 
 * @author Jack
 *
 */
public class DeadLock {
	public static void main(String[] args) {
		Markup g1 = new Markup(0, "欢欢");
		Markup g2 = new Markup(1, "婷婷");
		g1.start();
		g2.start();
	}
}

//口红
class Lipstick{
	
}

//镜子
class Mirror{
	
}

//化妆
class Markup extends Thread{
	static Lipstick lipstick = new Lipstick();
	static Mirror mirror = new Mirror();
	//选择
	int choice;
	//名字
	String girl;
	
	public Markup(int choice, String girl) {
		this.choice = choice;
		this.girl = girl;
	}

	@Override
	public void run() {
		//化妆
		markup();
	}

	//相互持有对方的对象锁,可能造成死锁
	private void markup() {
		if(choice==0){
			synchronized (lipstick) {
				//获得口红的锁
				System.out.println(this.girl+"涂口红");
				//1秒钟后想拥有镜子的锁
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (mirror) {
					System.out.println(this.girl+"照镜子");
				}
			}
		}else{
			synchronized (mirror) {
				//获得镜子的锁
				System.out.println(this.girl+"照镜子");
				//1秒钟后想拥有口红的锁
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				synchronized (lipstick) {
					System.out.println(this.girl+"涂口红");
				}
			}
		}
	}
}

在这里插入图片描述

避免死锁:不要再同一个代码块中同时持有多个对象的锁

//相互持有对方的对象锁,可能造成死锁
	private void markup() {
		if(choice==0){
			synchronized (lipstick) {
				//获得口红的锁
				System.out.println(this.girl+"涂口红");
				//1秒钟后想拥有镜子的锁
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			synchronized (mirror) {
				System.out.println(this.girl+"照镜子");
			}
		}else{
			synchronized (mirror) {
				//获得镜子的锁
				System.out.println(this.girl+"照镜子");
				//1秒钟后想拥有口红的锁
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			synchronized (lipstick) {
				System.out.println(this.girl+"涂口红");
			}
		}
	}

在这里插入图片描述

并发协作模型:生产者消费者模式–>01.管程法

/**
 * 协作模型:生产者消费者实现方式一:管程法
 * 借助缓冲区
 * @author Jack
 *
 */
public class CoTest01 {
	public static void main(String[] args) {
		//缓冲区
		SynContainer container = new SynContainer();
		new Productor(container).start();
		new Consumer(container).start();
	}
}


//生产者
class Productor extends Thread{
	SynContainer container;
	
	public Productor(SynContainer container) {
		this.container = container;
	}


	@Override
	public void run() {
		//生产
		for(int i=1;i<=10;i++){
			System.out.println("生产-->"+i+"个馒头");
			container.push(new Steamedbun(i));
		}
	}
}


//消费者
class Consumer extends Thread{
	SynContainer container;
	
	public Consumer(SynContainer container) {
		this.container = container;
	}

	@Override
	public void run() {
		//消费
		for(int i=1;i<=15;i++){
			System.out.println("消费-->"+container.pop().id+"个馒头");
		}
	}
}


//缓冲区
class SynContainer{
	Steamedbun[] buns = new Steamedbun[10];//存储容器
	int count = 0;//计数器
	//存储 生产
	public synchronized void push(Steamedbun bun){
		//何时能生产: 容器存在空间
		//不能生产: 只有等待
		if(count==buns.length){
			try {
				this.wait();//线程阻塞,生产者通知消费的时候解除阻塞
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//存在空间 可以生产
		buns[count] = bun;
		count++;
		this.notify();//存在数据了, 可以通知消费了
	}
	//获取 消费
	public synchronized Steamedbun pop(){
		//何时消费:容器中有数据的时候
		//没有数据:等待
		if(count==0){
			try {
				this.wait();//线程阻塞,生产者通知消费的时候解除阻塞
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//存在数据 可以消费
		count--;
		Steamedbun bun = buns[count];
		this.notify();//存在空间了,可以唤醒对方生产了
		return bun;
	}
}
//馒头
class Steamedbun{
	int id;

	public Steamedbun(int id) {
		this.id = id;
	}
}

在这里插入图片描述

并发协作模型:生产者消费者模式–>02.信号灯法

/**
 * 协作模式:生产者消费者模式实现方式二:信号灯法
 * 借助标志位
 * @author Jack
 *
 */
public class CoTest02 {
	public static void main(String[] args) {
		TV tv = new TV();
		new Player(tv).start();
		new Watcher(tv).start();
	}
}

//生产者 演员
class Player extends Thread{
	TV tv;

	public Player(TV tv) {
		this.tv = tv;
	}
	
	@Override
	public void run() {
		for(int i=0;i<5;i++){
			if(i%2==0){
				this.tv.play("奇葩说");
			}else{
				this.tv.play("太污了,来瓶立白洗洗嘴");
			}
		}
	}
}
//消费者 观众
class Watcher extends Thread{
	TV tv;

	public Watcher(TV tv) {
		this.tv = tv;
	}
	
	@Override
	public void run() {
		for(int i=0;i<5;i++){
			tv.watch();
		}
	}
}

//同一个资源 电视
class TV{
	String voice;
	//信号灯
	//T 表示演员表演 观众等待
	//F 表示观众观看 演员等待
	boolean flag = true;
	
	//表演
	public synchronized void play(String voice){
		//演员等待
		if(!flag){
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//表演
		System.out.println("表演了:"+voice);
		this.voice = voice;
		//唤醒
		this.notifyAll();
		//切换标识
		this.flag=!this.flag;
	}
	
	//观看
	public synchronized void watch(){
		//观众等待
		if(flag){
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//观看
		System.out.println("听到了:"+voice);
		//唤醒
		this.notifyAll();
		//切换标识
		this.flag=!this.flag;
	}
}

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值