多线程的线程安全与互斥解决方案——同步代码块/同步函数

时间:2021-2-27 作者:admin

文章目录

一、线程安全与互斥(案例说明)

1.经典案例

1、有100张电影票
2、设置三个窗口同时卖电影票
3、观察卖电影票的过程

2.代码实现

代码如下(SellTicket线程):

public class SellTicket implements Runnable {
	// 定义100张票
	private int tickets = 100;

	@Override
	public void run() {
		while (tickets > 0) {
			String name = Thread.currentThread().getName();
			System.out.println(name + "正在出售第" + tickets + "票");
			tickets--;
		}
	}
}

代码如下(SellTicket线程测试):

public class Test02 {

	public static void main(String[] args) {
		SellTicket sellTicket = new SellTicket();
		Thread t1 = new Thread(sellTicket,"窗口1");
		Thread t2 = new Thread(sellTicket,"窗口2");
		Thread t3 = new Thread(sellTicket,"窗口3");
		t1.start();
		t2.start();
		t3.start();
	}
}

输出:
多线程的线程安全与互斥解决方案——同步代码块/同步函数

小结

观察SellTicket线程测试输出我圈出来的那部分出现了错误错误卖票,由于输出版面有限,其实还有一个错误就是可能会卖出负的票数

多个线程同时访问共享资源很可能出现线程安全问题

二、解决思路

对多操作共享的语句为了保证各线程都执行完成,在某线程执行过程中,其他线程不可以参与执行

1.解决办法——同步代码块

同步代码块中的锁对象可以是任意对象。在进入代码块时需要获得锁对象,如果得到锁对象则等待;在退出代码块时自动释放锁对象。
任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行结束后,该线程自然会释放对同步监视器对象的锁定。
在java程序运行中可以同任何对象作为同步监视器对象,只要保证共享资源的这几个线程锁的是同一同步监视对象即可。
语法:

		synchronized (this) {
			需要被同步的代码
		}

2.同步代码块(SellTicket线程)

代码如下(同步代码块(SellTicket线程)):

public class SellTicket implements Runnable {
	// 定义100张票
	private int tickets = 100;

	@Override
	public void run() {
		// 同步代码块(加锁)
		synchronized (this) { 
			while (tickets > 0) {
				String name = Thread.currentThread().getName();
				System.out.println(name + "正在出售第" + tickets + "票");
				tickets--;
			}
		}
	}
}

代码如下(同步代码块(SellTicket线程)测试):

public class Test02 {

	public static void main(String[] args) {
		SellTicket sellTicket = new SellTicket();
		Thread t1 = new Thread(sellTicket,"窗口1");
		Thread t2 = new Thread(sellTicket,"窗口2");
		Thread t3 = new Thread(sellTicket,"窗口3");
		t1.start();
		t2.start();
		t3.start();
	}
}

代码如下(同步代码块(SellTicket线程)测试输出):

窗口1正在出售第100票
窗口1正在出售第99票
窗口1正在出售第98票
窗口1正在出售第97票
窗口1正在出售第96票
窗口1正在出售第95票
窗口1正在出售第94票
窗口1正在出售第93票
窗口1正在出售第92票
窗口1正在出售第91票
窗口1正在出售第90票
窗口1正在出售第89票
窗口1正在出售第88票
窗口1正在出售第87票
窗口1正在出售第86票
窗口1正在出售第85票
窗口1正在出售第84票
窗口1正在出售第83票
窗口1正在出售第82票
窗口1正在出售第81票
窗口1正在出售第80票
窗口1正在出售第79票
窗口1正在出售第78票
窗口1正在出售第77票
窗口1正在出售第76票
窗口1正在出售第75票
窗口1正在出售第74票
窗口1正在出售第73票
窗口1正在出售第72票
窗口1正在出售第71票
窗口1正在出售第70票
窗口1正在出售第69票
窗口1正在出售第68票
窗口1正在出售第67票
窗口1正在出售第66票
窗口1正在出售第65票
窗口1正在出售第64票
窗口1正在出售第63票
窗口1正在出售第62票
窗口1正在出售第61票
窗口1正在出售第60票
窗口1正在出售第59票
窗口1正在出售第58票
窗口1正在出售第57票
窗口1正在出售第56票
窗口1正在出售第55票
窗口1正在出售第54票
窗口1正在出售第53票
窗口1正在出售第52票
窗口1正在出售第51票
窗口1正在出售第50票
窗口1正在出售第49票
窗口1正在出售第48票
窗口1正在出售第47票
窗口1正在出售第46票
窗口1正在出售第45票
窗口1正在出售第44票
窗口1正在出售第43票
窗口1正在出售第42票
窗口1正在出售第41票
窗口1正在出售第40票
窗口1正在出售第39票
窗口1正在出售第38票
窗口1正在出售第37票
窗口1正在出售第36票
窗口1正在出售第35票
窗口1正在出售第34票
窗口1正在出售第33票
窗口1正在出售第32票
窗口1正在出售第31票
窗口1正在出售第30票
窗口1正在出售第29票
窗口1正在出售第28票
窗口1正在出售第27票
窗口1正在出售第26票
窗口1正在出售第25票
窗口1正在出售第24票
窗口1正在出售第23票
窗口1正在出售第22票
窗口1正在出售第21票
窗口1正在出售第20票
窗口1正在出售第19票
窗口1正在出售第18票
窗口1正在出售第17票
窗口1正在出售第16票
窗口1正在出售第15票
窗口1正在出售第14票
窗口1正在出售第13票
窗口1正在出售第12票
窗口1正在出售第11票
窗口1正在出售第10票
窗口1正在出售第9票
窗口1正在出售第8票
窗口1正在出售第7票
窗口1正在出售第6票
窗口1正在出售第5票
窗口1正在出售第4票
窗口1正在出售第3票
窗口1正在出售第2票
窗口1正在出售第1

3.解决办法——同步函数

对于同步方法(函数)来说,无需显示指定同步监视器,静态方法的同步监视器对象是当前类的class对象,非静态方法的同步监视器对象调用当前方法的this对象
语法:

	synchronized 函数返回值 函数名([参数列表]) {
		需要被同步的代码块
	}

2.同步函数(SellTicket线程)

代码如下(同步函数(SellTicket线程)):

public class SellTicket implements Runnable {
	// 定义100张票
	private int tickets = 2000;

	@Override
	public void run() {
			while (true) {
				sellTicket();
		}
	}
	// 同步函数(加锁)
	public synchronized void sellTicket() {
		if(tickets>0) {
			String name = Thread.currentThread().getName();
			System.out.println(name + "正在出售第" + tickets + "票");
			tickets--;
		}
	}
}

代码如下(同步函数(SellTicket线程)测试):

public class Test02 {

	public static void main(String[] args) {
		SellTicket sellTicket = new SellTicket();
		Thread t1 = new Thread(sellTicket,"窗口1");
		Thread t2 = new Thread(sellTicket,"窗口2");
		Thread t3 = new Thread(sellTicket,"窗口3");
		t1.start();
		t2.start();
		t3.start();
	}
}

代码如下(同步函数(SellTicket线程)测试输出):

窗口1正在出售第100票
窗口3正在出售第99票
窗口3正在出售第98票
窗口3正在出售第97票
窗口3正在出售第96票
窗口3正在出售第95票
窗口3正在出售第94票
窗口3正在出售第93票
窗口3正在出售第92票
窗口3正在出售第91票
窗口3正在出售第90票
窗口3正在出售第89票
窗口3正在出售第88票
窗口3正在出售第87票
窗口3正在出售第86票
窗口3正在出售第85票
窗口3正在出售第84票
窗口3正在出售第83票
窗口3正在出售第82票
窗口3正在出售第81票
窗口3正在出售第80票
窗口3正在出售第79票
窗口3正在出售第78票
窗口3正在出售第77票
窗口3正在出售第76票
窗口3正在出售第75票
窗口3正在出售第74票
窗口3正在出售第73票
窗口3正在出售第72票
窗口3正在出售第71票
窗口3正在出售第70票
窗口3正在出售第69票
窗口3正在出售第68票
窗口3正在出售第67票
窗口3正在出售第66票
窗口3正在出售第65票
窗口3正在出售第64票
窗口3正在出售第63票
窗口3正在出售第62票
窗口3正在出售第61票
窗口3正在出售第60票
窗口3正在出售第59票
窗口3正在出售第58票
窗口3正在出售第57票
窗口3正在出售第56票
窗口3正在出售第55票
窗口3正在出售第54票
窗口3正在出售第53票
窗口3正在出售第52票
窗口3正在出售第51票
窗口3正在出售第50票
窗口3正在出售第49票
窗口3正在出售第48票
窗口3正在出售第47票
窗口3正在出售第46票
窗口3正在出售第45票
窗口3正在出售第44票
窗口3正在出售第43票
窗口3正在出售第42票
窗口3正在出售第41票
窗口3正在出售第40票
窗口3正在出售第39票
窗口3正在出售第38票
窗口3正在出售第37票
窗口3正在出售第36票
窗口3正在出售第35票
窗口3正在出售第34票
窗口3正在出售第33票
窗口3正在出售第32票
窗口3正在出售第31票
窗口3正在出售第30票
窗口3正在出售第29票
窗口3正在出售第28票
窗口3正在出售第27票
窗口3正在出售第26票
窗口3正在出售第25票
窗口3正在出售第24票
窗口3正在出售第23票
窗口3正在出售第22票
窗口3正在出售第21票
窗口3正在出售第20票
窗口3正在出售第19票
窗口3正在出售第18票
窗口3正在出售第17票
窗口3正在出售第16票
窗口3正在出售第15票
窗口3正在出售第14票
窗口3正在出售第13票
窗口3正在出售第12票
窗口3正在出售第11票
窗口3正在出售第10票
窗口3正在出售第9票
窗口3正在出售第8票
窗口3正在出售第7票
窗口3正在出售第6票
窗口3正在出售第5票
窗口3正在出售第4票
窗口3正在出售第3票
窗口3正在出售第2票
窗口3正在出售第1

总结

1、同步提升了安全性,但是降低了效率
2、任意的一个对象都可以作为锁对象
3、多个线程操作共享数据的锁必须是同一个锁,否则加锁无效
4、只有真正存在线程安全问题的时候才使用同步代码块或者同步函数,否则会降低效率
5、多线程程序执行具有随机性,谁抢到就谁执行,测试输出就可以观察出来

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。