线程中资源(一)
2015-11-19 16:31
246 查看
对于单线程来说,因为只有一个实体,所以永远都不需要担心“两个实体同时使用同一个资源会发生什么”这样的问题。
有了并发就可以同时做多件事情了,但是两个或多个线程彼此互相干涉的问题也就出现了。这就是我们将要讨论的事
1.不正确地访问资源
如果几个线程同时读一个资源,此时不会更改资源,这时也不会需要考虑这个资源竞争这个问题。但是一旦涉及到写操作,这便会涉及到竞争资源这个问题,这时也会对读造成影响
2.解决共享资源竞争
对于并发工作,你需要某种方式来防止两个任务访问相同的资源,至少在关键阶段不能出现这种情况。
防止这种冲突的方法就是当资源被一个任务使用时,在其加上锁。第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,就无法访问它,而在其被解锁之时,另一个任务就可以锁定并使用它了。
共享资源一般是以对象形式存在的内存片段,但也可以是文件,输入/输出端口,或者是打印机。要控制对共享资源的访问,必须把它包装进一个对象。而我们会把它打上标记synchronized,下面是synchronized 的声明方式
所有对象都自动含有单一的锁。当在对象上调用其任意synchronized 方法的时候,此对象都被加锁,这时该对象上的其他synchronized 方法只有等到前一个方法调用完毕并释放了锁之后才能被调用。
注意,在使用并发时,将域要设置为private ,否则synchronized 关键字就不能防止其他任务直接访问域。
一个任务可以多次获得对象锁。如果一个方法在同一个对象上调用了第二个方法,后者又调用了同一对象上的另一个方法,就会发生这种情况。jvm负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数就变为0。在任务(注意这里我们用任务不用线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务在这个对象上获得锁时,计数都会 递增。显然,只有首页获得了锁的任务才能允许继续获取多个锁。每当任务离开一个synchronized 方法,计数递减,当计数为0的时候锁被完全释放,此时别的任务就可以使用这个资源。
针对每个类,也有一个锁,synchronized static方法可以在类的范围内防止对static数据的并发访问。
这个时候就有问题了,你应该什么时候同步呢?,这里要说一下Brain的同步规则:
如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么你必须使用同步,并且,读写进程都必须用相同的监视器同步。
如果在你的类中有超过一个方法正在处理临界数据,那么你必须同步所有相关的方法。如果只同步一个方法,那么其他方法将会随意地忽略这个对象锁,并可以在无任何惩罚的情况下被调用。这是很重要的一点:每个访问临界共享资源的方法都必须被同步,否则它们就不会正确地工作。
使用显示的Lock对象
在这里我们显示的用lock()和unlock()方法,注意惯用顺序:
开始对lock()的调用,然后在try里面做自己的工作,return必须在try子句中出现,以确保unlock()不会过早发生,从而将数据暴露给了第二个任务。
虽然try-finally所需的代码比synchronized关键字要多,但是这也代表了显示的Lock对象的优点之一。如果在使用synchronized关键字时,某些事物失败了就会抛出一个异常。但是你没有机会去做任何清理工作,以维护系统使其处于良好状态。有了显示的Lock对象,你就可以使用finally子句将系统维护在正确地状态了。
一般解决特殊问题时,才显示的Lock对象。
有了并发就可以同时做多件事情了,但是两个或多个线程彼此互相干涉的问题也就出现了。这就是我们将要讨论的事
1.不正确地访问资源
如果几个线程同时读一个资源,此时不会更改资源,这时也不会需要考虑这个资源竞争这个问题。但是一旦涉及到写操作,这便会涉及到竞争资源这个问题,这时也会对读造成影响
2.解决共享资源竞争
对于并发工作,你需要某种方式来防止两个任务访问相同的资源,至少在关键阶段不能出现这种情况。
防止这种冲突的方法就是当资源被一个任务使用时,在其加上锁。第一个访问某项资源的任务必须锁定这项资源,使其他任务在其被解锁之前,就无法访问它,而在其被解锁之时,另一个任务就可以锁定并使用它了。
共享资源一般是以对象形式存在的内存片段,但也可以是文件,输入/输出端口,或者是打印机。要控制对共享资源的访问,必须把它包装进一个对象。而我们会把它打上标记synchronized,下面是synchronized 的声明方式
synchronized void f(){}
所有对象都自动含有单一的锁。当在对象上调用其任意synchronized 方法的时候,此对象都被加锁,这时该对象上的其他synchronized 方法只有等到前一个方法调用完毕并释放了锁之后才能被调用。
注意,在使用并发时,将域要设置为private ,否则synchronized 关键字就不能防止其他任务直接访问域。
一个任务可以多次获得对象锁。如果一个方法在同一个对象上调用了第二个方法,后者又调用了同一对象上的另一个方法,就会发生这种情况。jvm负责跟踪对象被加锁的次数。如果一个对象被解锁,其计数就变为0。在任务(注意这里我们用任务不用线程)第一次给对象加锁的时候,计数变为1。每当这个相同的任务在这个对象上获得锁时,计数都会 递增。显然,只有首页获得了锁的任务才能允许继续获取多个锁。每当任务离开一个synchronized 方法,计数递减,当计数为0的时候锁被完全释放,此时别的任务就可以使用这个资源。
针对每个类,也有一个锁,synchronized static方法可以在类的范围内防止对static数据的并发访问。
这个时候就有问题了,你应该什么时候同步呢?,这里要说一下Brain的同步规则:
如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么你必须使用同步,并且,读写进程都必须用相同的监视器同步。
如果在你的类中有超过一个方法正在处理临界数据,那么你必须同步所有相关的方法。如果只同步一个方法,那么其他方法将会随意地忽略这个对象锁,并可以在无任何惩罚的情况下被调用。这是很重要的一点:每个访问临界共享资源的方法都必须被同步,否则它们就不会正确地工作。
使用显示的Lock对象
private Lock lock=new ReentrantLock(); @Override public int next() { lock.lock(); try { ++currentEvenValue; ++currentEvenValue; return currentEvenValue; } finally { lock.unlock(); } }
在这里我们显示的用lock()和unlock()方法,注意惯用顺序:
开始对lock()的调用,然后在try里面做自己的工作,return必须在try子句中出现,以确保unlock()不会过早发生,从而将数据暴露给了第二个任务。
虽然try-finally所需的代码比synchronized关键字要多,但是这也代表了显示的Lock对象的优点之一。如果在使用synchronized关键字时,某些事物失败了就会抛出一个异常。但是你没有机会去做任何清理工作,以维护系统使其处于良好状态。有了显示的Lock对象,你就可以使用finally子句将系统维护在正确地状态了。
一般解决特殊问题时,才显示的Lock对象。
相关文章推荐
- matlab实现批量图片的直方图均衡化
- node.js中的控制台
- Python 主要模块和常用方法简览
- linux下设置opencv环境变量
- PHP读取日志里数据方法理解
- 缓冲区溢出攻击实验(三)
- java 死锁
- 2015-11-19 100及以上数字 英文字母
- C语言学习笔记(17) 数组和指针分析
- shell脚本报错:-bash: xxx: /bin/sh^M: bad interpreter: No such file or directory
- Normalized Mutual Information (2013-05-20 15:22:39)
- mysql : utf8mb4 的问题
- AJAX获取JSON形式的数据
- xcode7无证书真机调试
- Android开发,关于引起内存泄露的资源汇总
- Option类型
- 支付宝调试成功
- Android.mk 小结
- 6个值得推荐的Android开源框架简介
- 十四个很准的心理暗示