线程中资源(一)

来源:互联网 发布:厦门鼓浪屿住宿知乎 编辑:程序博客网 时间:2024/06/16 08:12

  对于单线程来说,因为只有一个实体,所以永远都不需要担心“两个实体同时使用同一个资源会发生什么”这样的问题。
  有了并发就可以同时做多件事情了,但是两个或多个线程彼此互相干涉的问题也就出现了。这就是我们将要讨论的事
  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对象。

0 0
原创粉丝点击