并发同步

来源:互联网 发布:移动互联网数据 编辑:程序博客网 时间:2024/04/28 19:56

产生的原因的是:线程通信是通过分享数据和对象的访问,这就导致了两个问题:线程干涉和内存一执性错误。可以用来阻止这些错误的工具是同步。

然而,同步可能引入线程竞争,当两个或多个线程尝试去同时获取一个资源,并且导致虚拟机执行一个或多个线程更慢,或者甚至挂起他们的执行。

Starvation和livelock是线程竞争的形式。

同步主要讲:

线程干扰,描述当多个线程访问共享数据错误是如何引入的。

比如下面这个类:

class Counter {    private int c = 0;    public void increment() {        c++;    }    public void decrement() {        c--;    }    public int value() {        return c;    }}
因为虚拟机对自增操作是分成三步的:


  1. Retrieve the current value of c.
  2. Increment the retrieved value by 1.
  3. Store the incremented value back in c.
所以,两个线程A,B的操作有可能是这样的:

Suppose Thread A invokes increment at about the same time Thread B invokes decrement. If the initial value of c is 0, their interleaved actions might follow this sequence:

  1. Thread A: Retrieve c.
  2. Thread B: Retrieve c.
  3. Thread A: Increment retrieved value; result is 1.
  4. Thread B: Decrement retrieved value; result is -1.
  5. Thread A: Store result in c; c is now 1.
  6. Thread B: Store result in c; c is now -1.
我们会发现,这样线程A的自增结果被丢失了,实际上是被B覆盖了。这些都是不可预期的,所以进程干扰的bug很难发现和修复。


内存一执性错误  描述由不兼容视角的共享内存导致的错误,就是在不同角度看共享内存,不一致的问题?

导致这个问题的原因很复杂,我们现在仅仅是需要知道 happen-before原则。

假设,int counter = 0;

A,B两个线程分享couter,A执行自增操作

counter++;

然后B打印counter

如果是在同一个进程执行,肯定是打印1 ,但是在不同的进程间,有可能是0.因为A的改变不能保证B能看见,除非程序员建立一个良好的happen-before关系在这两个语句间。

两个动作产生happer-before:

  • When a statement invokes Thread.start, every statement that has a happens-before relationship with that statement also has a happens-before relationship with every statement executed by the new thread. The effects of the code that led up to the creation of the new thread are visible to the new thread.
  • When a thread terminates and causes a Thread.join in another thread to return, then all the statements executed by the terminated thread have a happens-before relationship with all the statements following the successful join. The effects of the code in the thread are now visible to the thread that performed the join.

一个是,new Thread start之前的代码,比Thread中的先发生。

一个是调用Thread.join的线程的代码,先于执行join的这个进程。


然后我们可以使用Synchronized关键字,这个关键字两个用处

(1)如果一个线程执行这个方法,那么另一个线程就无法执行,只有等待第一个线程执行完成

(2)synchronized关键字自动建立happen-before,保证改变对所有线程可见。

但是有一个问题就是 liveness??  生命安全问题,比如死锁。






0 0