buder

201709021工作日记--线程安全与不安全、保证线程安全的三种方案、互斥和信号

1.什么是线程安全和线程不安全

  首先要明白线程的工作原理,jvm有一个main  memory,而每个线程有自己的working  memory,一个线程对一个variable进行操作时,都要在自己的working  memory里面建立一个copy,操作完之后再写入main  memory。多个线程同时操作同一个variable,就可能会出现不可预知的结果。根据上面的解释,很容易想出相应的scenario。  

  而用synchronized的关键是建立一个monitor,这个monitor可以是要修改的variable也可以其他你认为合适的object比如method,然后通过给这个monitor加锁来实现线程安全,每个线程在获得这个锁之后,要执行完  

load到workingmemory   ->   use&assign   ->   store到mainmemory   的过程,才会释放它得到的锁。这样就实现了所谓的线程安全。

  线程安全,指的是在并发性问题中资源的使用问题。当某个线程在使用某个资源的使用,其它线程必须等它使用完毕才可以使用.静态方法是在静态存储区因为它不需要实例化所以是唯一的.而其它实例方法由多个实例来调用,所以必须要考虑“线程安全”问题.

  线程安全是为了避免数据竞争--数据设置的正确性依赖于多个线程修改数据的顺序。 
  如果不需要共享,则给每个线程分配一个私有的数据拷贝。如果数据必须共享,一定要用同步机制来保证操作的唯一性。

2. 保证线程安全的三种方案

 

  • 阻塞同步/互斥同步,最常见的并发正确性保障手段,同步至多个线程并发访问共享数据时,保证共享数据在同一个时刻只被一个线程使用。

  • 非阻塞同步,互斥同步的主要问题就是进行线程的阻塞和唤醒所带来的性能问题,因此这个同步也被称为阻塞同步,阻塞同步属于一种悲观的并发策略,认为只要不去做正确的同步措施,就肯定会出问题,无论共享的数据是否会出现竞争。随着硬件指令的发展,有了另外一个选择,基于冲突检测的乐观并发策略,通俗的讲就是先进性操作,如果没有其他线程争用共享数据,那操作就成功了,如果共享数据有争用,产生了冲突,那就再进行其他的补偿措施(最常见的措施就是不断的重试,直到成功为止),这种策略不需要把线程挂起,所以这种同步也被称为非阻塞同步。

  • 无同步方案,简单的理解就是没有共享变量需要不同的线程去争用,目前有两种方案,一个是“可重入代码”,这种代码可以在执行的任何时刻中断它,转而去执行其他的另外一段代码,当控制权返回时,程序继续执行,不会出现任何错误。一个是“线程本地存储”,如果变量要被多线程访问,可以使用volatile关键字来声明它为“易变的“,以此来实现多线程之间的可见性。同时也可以通过ThreadLocal来实现线程本地存储的功能,一个线程的Thread对象中都有一个ThreadLocalMap对象,来实现KV数据的存储。 

3.互斥量和信号量的区别

  这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。

  互斥量:这是多线程互斥用的,比如说,一个线程占用了某一个资源,那么别的线程就无法访问,知道这个线程离开,其他的线程才开始可以利用这个资源。

  信号量:那是多线程同步用的,一个线程完成了某一个动作就通过信号告诉别的线程,别的线程再进行某些动作。

 

1. 互斥量用于线程的互斥,信号量用于线程的同步。  这是互斥量和信号量的根本区别,也就是互斥和同步之间的区别。  

  互斥:是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。  

  同步:是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源  

2. 互斥量值只能为0/1,信号量值可以为非负整数。  

也就是说,一个互斥量只能用于一个资源的互斥访问,它不能实现多个资源的多线程互斥问题。信号量可以实现多个同类资源的多线程互斥和同步。当信号量为单值信号量是,也可以完成一个资源的互斥访问。  

3. 互斥量的加锁和解锁必须由同一线程分别对应使用,信号量可以由一个线程释放,另一个线程得到。

信号量(Semaphore)

信号量,有时也称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确、合理的使用公共资源。 

信号量可以分为几类:  

二进制信号量(binarysemaphore):只允许信号量取0或1值,其同时只能被一个线程获取。  

整型信号量(integersemaphore):信号量取值是整数,它可以被多个线程同时获得,直到信号量的值变为0。  

记录型信号量(recordsemaphore):每个信号量s除一个整数值value(计数)外,还有一个等待队列List,其中是阻塞在该信号量的各个线程的标识。当信号量被释放一个,值被加一后,系统自动从等待队列中唤醒一个等待中的线程,让其获得信号量,同时信号量再减一。  

信号量通过一个计数器控制对共享资源的访问,信号量的值是一个非负整数,所有通过它的线程都会将该整数减一。如果计数器大于0,则访问被允许,计数器减1;如果为0,则访问被禁止,所有试图通过它的线程都将处于等待状态。  

计数器计算的结果是允许访问共享资源的通行证。因此,为了访问共享资源,线程必须从信号量得到通行证, 如果该信号量的计数大于0,则此线程获得一个通行证,这将导致信号量的计数递减,否则,此线程将阻塞直到获得一个通行证为止。当此线程不再需要访问共享资源时,它释放该通行证,这导致信号量的计数递增,如果另一个线程等待通行证,则那个线程将在那时获得通行证。  

Semaphore可以被抽象为五个操作:  

-创建Create  

-等待 Wait: 线程等待信号量,如果值大于0,则获得,值减一;如果只等于0,则一直线程进入睡眠状态,知道信号量值大于0或者超时。  

-释放 Post:执行释放信号量,则值加一;如果此时有正在等待的线程,则唤醒该线程。  

-试图等待TryWait:如果调用TryWait,线程并不真正的去获得信号量,还是检查信号量是否能够被获得,如果信号量值大于0,则TryWait返回成功;否则返回失败。  

-销毁Destroy  

 

互斥量(Mutex)  

互斥量表现互斥现象的数据结构,也被当作二元信号灯。一个互斥基本上是一个多任务敏感的二元信号,它能用作同步多任务的行为,它常用作保护从中断来的临界段代码并且在共享同步使用的资源。  

Mutex本质上说就是一把锁,提供对资源的独占访问,所以Mutex主要的作用是用于互斥。Mutex对象的值,只有0和1两个值。这两个值也分别代表了Mutex的两种状态。值为0, 表示锁定状态,当前对象被锁定,用户进程/线程如果试图Lock临界资源,则进入排队等待;值为1,表示空闲状态,当前对象为空闲,用户进程/线程可以Lock临界资源,之后Mutex值减1变为0。  

Mutex可以被抽象为四个操作:

-创建Create

-加锁 Lock

-解锁Unlock

-销毁Destroy  

Mutex被创建时可以有初始值,表示Mutex被创建后,是锁定状态还是空闲状态。在同一个线程中,为了防止死锁,系统不允许连续两次对Mutex加锁(系统一般会在第二次调用立刻返回)。也就是说,加锁和解锁这两个对应的操作,需要在同一个线程中完成。

posted on 2017-09-21 09:21  buder  阅读(212)  评论(0)    收藏  举报

导航