【Java】多线程、线程同步、线程通信、线程调度与死锁(2)

电脑杂谈  发布时间:2019-06-22 05:17:01  来源:网络整理

说一句大逆不道的话,如果程序里需要解决如“哲学家就餐”之类的复杂 ipc 问题,我认为应该首先考察几个设计,为什么线程之间会有如此复杂的资源争抢(一个线程要同时抢到两个资源,一个资源可以被两个线程争夺)。说一句大逆不道的话,如果程序里需要解决如“哲学家就餐”之类的复杂ipc问题,我认为应该首先考察几个设计,为什么线程之间会有如此复杂的资源争抢(一个线程要同时抢到两个资源,一个资源可以被两个线程争夺)。假如,下载tickets=1,a线程刚执行完①语句,还没开始执行②语句时,cpu资源就被b线程抢夺去了,b线程执行“if(tickets>0)”,因为在此时num还是1,所以b线程将执行①语句,在“--tickets”之后,ticket的数量就变成0,而此时cpu资源就被a线程抢夺去了,接着还没有执行的②语句时,由于此时“ticket=0”,在执行完②语句后,ticket的数量就变成-1了。

public class TestThread {
	public static void main(String[] args) {
		Bank bank = new Bank();
		bank.getMoney(1500);
		Thread person1 = new Thread(bank);
		person1.setName("==person1==");
		person1.start();
		try {
			person1.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		bank.getMoney(2000);
		Thread person2 = new Thread(bank);
		person2.setName("==person2==");
		person2.start();
		try {
			person2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		bank.getMoney(3000);
		Thread person3 = new Thread(bank);
		person3.setName("==person3==");
		person3.start();
		try {
			person3.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}
class Bank implements Runnable {
	int totalMoney = 10000;
	int money;// 要取出的钱
	public void getMoney(int money) {
		this.money = money;
	}
	public void run() {
		String name = Thread.currentThread().getName();
		System.out.println(name + "取出:" + money);
		int remainMoney = totalMoney - money;
		totalMoney = remainMoney;
		System.out.println("银行剩余:" + remainMoney);
	}
}

如果注释上面代码中线程的 join() 方法,意味着三个人对cpu的获取权一样大,比如第一个人取到一半java多线程死锁,执行权被第二个人给抢了,这就会导致金钱出现异常。

随着硬件指令集的发展,我们有另外的选择:基于冲突检测的乐观并发策略,通俗讲就是先操作,如果没有其他线程争用共享的数据,操作就成功,如果有,则进行其他的补偿(最常见就是不断的重试),这种乐观的并发策略许多实现都不需要把线程挂起,这种同步操作被称为非阻塞同步。多线程资源控制,由于线程之间共享进程中的资源,所以同一进程中的多个线程的通讯的重心之一是对共享的资源进行控制,例如当多个线程同时使用进程某个资源时,有可能需要采用互斥/同步等手段保证资源被正确操作,在采用互斥/同步手段时候,需要避免产生死锁的情况。在并发编程中,需要处理两个关键问题:线程之间如何通信及线程之间如何同步(这里的线程是指并发执行的活动实体)。

举个实际中的例子,我在乘坐高铁时想上厕所,这时厕所显示绿色,发现厕所没人用,我就进去了,却不小心门没有关紧。这时又来了一位想上厕所的人,由于门没关好,厕所上面的灯是绿色的,所以这位后面来的人就开门进来了java多线程死锁,这就导致厕所紊乱了。

java多线程死锁_java线程死锁_linux 线程死锁

用这个例子反证线程的执行过程,简直一模一样。这个厕所,就如线程处理的同一份资源。多个人就对应多个线程,在同时处理一份资源时,问题就来了。

互斥锁(synchronized),这是一个关键字。作用在同一份资源上时,就是相当于厕所上面的指示器的作用,给这个资源加上一把锁,你其他线程不许进来,等我处理结束后再说。

看一个例子:

一家电影院有三个售票窗口,这部电影共有30个座位(30张票)。如果三个窗口同时卖票,则该如何操作?

public class TestThread {
	public static void main(String[] args) {
		Cinema cinema = new Cinema();
		Thread window1 = new Thread(cinema);
		window1.setName("==窗口1==");
		window1.start();
		Thread window2 = new Thread(cinema);
		window2.setName("==窗口2==");
		window2.start();
		Thread window3 = new Thread(cinema);
		window3.setName("==窗口3==");
		window3.start();
	}
}
class Cinema implements Runnable {
	int ticket = 30;
	public void run() {
		String name = Thread.currentThread().getName();
		while (true) {
			if (ticket > 0) {
				try {
					Thread.currentThread().sleep(50);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(name + "售出" + ticket-- + "号座位");
			}
		}
	}
}

从打印结果可以看出,不仅出现了重复座位,而且还出现了 0 号座位,存在极大的bug。出现bug的原因:多个线程参与同一个数据的操作,如上代码中,多个线程同时卖30张票,却没有给操作同一个资源加锁,就会出现这种bug。

join与synchronized的区别是:join内部是使用wait()方法进行等待的,而synchronized关键字是使用的是“对象监视浓ky"/kf/ware/vc/" target="_blank">vcmcmrxdw871k3a7df2zqrnrlk9oam8l3a+dqo8adigawq9"方法join与异常">方法join与异常:。这里我们使用反射的方式创建对象,替换了之前的工厂类通过switch创建对象的方式,有利于后面的新类型商店增加和算法修改增加和维护以后在项目需求在有变革,我们只需要重新在项目中增加c,d类型商店,继承ishop实现里面的方法,同时,添加继承ifactroy接口,创建对应的商店对象编译该项目的productenity.dll,以后再进行计算该c,d类型的商店算法就可以通过反射的方式进行计算,不需要修改原来的工程类代码。高级,写出注册机二.用w32dasm破解的一般步骤:1.看软件的说明书,软件注册与不注册在功能上有什么区别,如何注册2.运行此软件,试着输入你的姓名和任意注册码去注册,有什麽错误提示信息,将错误提示信息记下来3.侦测有无加壳(第一课).若加壳,脱壳(第二课)4.pw32dasmgold反汇编5.串式参考中找到错误提示信息或可能是正确的提示信息双击鼠标左键6.pw32dasmgold主窗口中分析相应汇编,找出关键跳转和关键call7.绿色光条停在关键跳转,在pw32dasmgold主窗口底部找到关键跳转的偏移地址(实际修改地址)8.用ultraedit找到偏移地址(实际修改地址)修改机器码,保存9.用w32dasm破解成功第六课w32dasm破解实例(一)***************************破解对象:ztz-ie网络浏览器1.7下载(也可到站点搜一下,找到1.7版很容易)。

class Cinema implements Runnable {
	int ticket = 30;
	public void run() {
		while (true) {
			synchronized (this) {
				if (ticket > 0) {
					try {
						Thread.currentThread().sleep(50);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(Thread.currentThread().getName() + "售出" + ticket-- + "号座位");
				}else {
					break;
				}
			}
		}
	}
}
class Cinema implements Runnable {
	int ticket = 30;
	boolean flag = true;
	public void run() {
		while (flag) {
			sell();
		}
	}
	public synchronized void sell() {
		if (ticket > 0) {
			try {
				Thread.currentThread().sleep(50);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "售出" + ticket-- + "号座位");
			flag = true;
		} else {
			flag = false;
		}
	}
}

使用同步技术可以解决这种问题, 把操作数据的代码进行同步, 不要多个线程一起操作。semaphore:保证不超过某个数量的线程可以存取同一个资源,或操作一段代码。上面程序使用@synchronized将draw:方法的方法体修改成同步代码块,该同步代码块的同步监视器是lcaccount对象本声,这样做法符合”加锁→修改→释放锁”的逻辑,任何线程在修改制定资源之前,首先都要对该资源加锁,在加锁期间其他线程无法修改该资源,当该线程修改完成后,释放对该资源的锁定.通过这种方式就可以保证并发线程在任一时刻只有一个线程可以进入修改共享资源的代码区(也称为临界区),所以同一时刻最多只有一个线程处于临界区内,从而保证了线程的安全性.。

结果正常:


本文来自电脑杂谈,转载请注明本文网址:
https://www.pc-fly.com/a/jisuanjixue/article-107571-2.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    热点图片
    拼命载入中...