Java语言:多线程之线程同步和线程间通信(银行存取款问题)
目录
线程同步
线程同步问题,重点是同步这两个字
在多线程的运行中,存在这样一些问题
1、各个线程通过竞争,从而获得CPU的时间片,获得时间片的机会是随机的
2、各个线程在运行的时候,到底会占用多久的时间片,也是未知的,意思就是说
,你有可能整个线程并没有执行完,会被其他的线程插一脚,这样非常有可能导致数据的不一致
下面举一个例子
这个例子讲的是银行存取款问题,这是一个经典问题(我只是简单的讲一下这个问题
,如果记得没错的话,这部分的知识,是操作系统的知识)
银行存取款问题就是一个公司只有一个账户,然后多人对其进行操作,有可能会有同时操作的可能
直接上代码吧!
bank类
存款方法
在存款和取款方法中分别加入了sleep方法
这里是为了模拟线程在执行中进入阻塞状态
取款方法
存款线程类
取款线程类
测试类
这里加上了join方法的原因是
想要bank对象能够在最后打印
因为在哪个方法中调用线程,哪个方法就会被挂起来
为什么会出现上面的问题呢?
我的分析如下
可能在save对象开始操作的时候,操作完balance之后,balance值为1100
,没有调用setBalance方法将数据及时存取,却被打断
然后draw对象开始操作,draw对象操作完balance之后,balance值为800
,但是此时线程又被打断
save对象操作的线程继续执行,调用setBalance方法,将值1100写进balance中
然后draw对象操作的线程就开始操作,调用setBalance方法,将值800写进线程中
这样最后balance的值就是800
但是因为两个存取款方法都有输入输出,所有输入输出的结果不一样
出现上面这个问题最大的问题就是线程在执行的过程中被中断,进入阻塞状态
所以只要解决这个问题即可
所以引入了synchronized关键字
synchronized可以确保共享对象在同一时刻只能被一个线程访问
下面是synchronized的使用方法,如下图所示
语句块中的obj是要执行同步的对象
将存取款方法改了一下,如下图
取款方法
取款方法
改完之后,运行结果如下
线程间通信
银行存取款问题描述如下
下面这张图将银行的存取款问题用消费者、生产者和一个中间存储介质代替
银行存取款问题的中心思想就是生产一个消费一个
生产者类
消费者类
Queue类
测试类
运行结果
这里你会发现一个问题,就是会出现连续的生产,或者是连续的消费,如上图
所以这里要设置一个标志,告诉消费者,里面有钱才可以消费,告诉生产者,里面有钱就不需要生产
因此
这里设置一个flag
如果flag是true
那么就可以让消费者进行消费
如果flag为flase
那么说明还没有生产,就不可以消费
那么你的消费的方法需要等待
同理当flag为true的时候
生产者也不可以继续生产
也要进入等待
等待由wait方法执行
等待其实是阻塞的状态
当消费者在等待,生产者也在等待
那么就会陷入死锁的状态
因此要进行唤醒
这里就要提到几个方法,如下
对消费者和生产者类中的run方法进行修改如下,加上flag和唤醒方法
生产者和消费者类还要加上wait方法,用于等待,这样既可
运行结果如下
对于这个问题主要就是有两个点
第一个是死锁的问题,因为使用到了wait方法,所以要使用notifyAll方法唤醒所有的线程
第二个问题是让其生产一次消费一次的问题,这里就要用到一个标志符flag
第三个问题就是同步的问题,因为如果不写同步,可能会连续生产10个,再消费一个
也有可能还没有生产就开始消费,这里可以使用synchronized关键字解决这个问题
自己还是觉得这篇文章没有讲清楚,自己有时间会再改进的