多线程安全

线程安全性问题

多线程环境下
多个线程共享一个资源
对资源进行非原子性操作

线程所带来的风险

线程安全性问题
活跃性问题(死锁,饥饿,活锁)

性能问题

 1 public class Sequence {
 2     private int value;
 3 
 4     
 5     public  int getNext() { 
 6     return value ++;
 7 }
 8 
 9 //    public synchronized int getNext() { //加synchronized,同一时刻,只有一个线程在执行
10 //        return value ++;
11 //    }
12     
13     public static void main(String[] args) {
14         Sequence s = new Sequence();
15         
16         
17         new Thread(new Runnable() {
18 
19             @Override
20             public void run() {
21                 while(true) {
22                     System.out.println(Thread.currentThread().getName()+"  "+s.getNext());
23                     try {
24                         Thread.sleep(100);
25                     } catch (InterruptedException e) {
26                         // TODO Auto-generated catch block
27                         e.printStackTrace();
28                     }
29                 }
30             }
31         }).start();
32         
33         new Thread(new Runnable() {
34 
35             @Override
36             public void run() {
37                 while(true) {
38                     System.out.println(Thread.currentThread().getName()+"  "+s.getNext());
39                     try {
40                         Thread.sleep(100);
41                     } catch (InterruptedException e) {
42                         // TODO Auto-generated catch block
43                         e.printStackTrace();
44                     }
45                 }
46             }
47         }).start();
48         
49         new Thread(new Runnable() {
50 
51             @Override
52             public void run() {
53                 while(true) {
54                     System.out.println(Thread.currentThread().getName()+"  "+s.getNext());
55                     try {
56                         Thread.sleep(100);
57                     } catch (InterruptedException e) {
58                         // TODO Auto-generated catch block
59                         e.printStackTrace();
60                     }
61                 }
62             }
63         }).start();
64 
65     }
66 
67 }

首先我们知道类的实例化对象是在堆内存中的,堆属于线程所共享的区域,程序计数器是线程独享的区域,value变量属于多个线程共享的区域。

0 -> 第一个线程执行后为 iadd 显示为一,但未(puffield)设置
  同时第二个线程 getfield获取值,得到为0,抢到时间片执行 iadd显示为一
第一个线程抢到时间片 执行(puffield),为一
第二个线程执行(puffield),为一,即所谓线程安全性问题

synchronized 关键字提供了一种锁的机制,能够确保共享变量的互斥访问,从而防止数据数据不一致问题的出现

synchronized 关键字包括monitor enter 和monitor exit 两个JVM指令,它保证在任何时候任何线程执行到monitor enter 成功之前都必须从主内存中获取数据,而不是在缓存中,在monitor exit 运行成功之后,共享变量被更新后的值必须刷入主内存

synchronized 放在普通方法上,内置锁就是当前类的实例

修饰静态方法,内置锁是当前的Class字节码对象

修饰代码块

任何对象都可以作为锁,锁信息存在对象头中

对象头中的信息,

  • Mark Word  // 
  • Class Metadata Address 
  • Array Length

饥饿与公平
1,高优先级吞噬所有低优先级的CPU时间片2,线程被永久堵塞在一个等待进入同步块的状态3,等待的线程永远不被唤醒
如何尽量避免饥饿问题1,设置合理的优先级2,使用锁来代替synchronized(偏向锁,轻量级锁,重量级锁)

 1 public class Demo1 {
 2     public static void main(String[] args) {
 3         
 4         
 5         Thread t1 = new Thread(new Target());
 6         Thread t2 = new Thread(new Target());
 7         
 8         t1.setPriority(10); //1<=5<=10
 9         t2.setPriority(1);
10         
11         t1.start();
12         t2.start();
13     }
14 }
public class Target implements Runnable{

    @Override
    public void run() {

        while(true) {
            System.out.println(Thread.currentThread().getName()+" ... ");
        }
        
        
    }

}

//执行如下:
//Thread-0 ...
//Thread-0 ...
//Thread-0 ...
//Thread-0 ...

 偏向锁

每次获取锁和释放锁会浪费资源
很多情况下,竞争锁不是由多个线程,而是由一个线程在使用。

第一次线程进入后,会检查锁标志位,是否是偏向锁,检查线程id,如果和当前线程id一致,就不用再获取

第二次进入,其并没有释放锁,一直在运行,接着运行,没有锁的获取和释放的过程,锁的撤销则等到竞争出现才释放索的机制,适用于只有一个线程访问代码块的场景

轻量级锁

同时让多个线程进入同步代码块中,同时获取锁,JVM会在当前线程的栈帧中创建用于存储锁记录的空间,并把对象头中的Mark Word复制到锁记录空间中,开始竞争,成功之后锁标志位改为轻量级锁,轻量级锁所适应的场景是线程交替执行同步块的场合,如果存在同一时间访问同一锁的场合,就会导致轻量级锁膨胀为重量级锁。

 

 

线程安全性问题总结

 

出现线程安全性问题的条件

 

在多线程的环境下

 

必须有共享资源

 

对共享资源进行非原子性操作

 

解决线程安全性问题的途径

 

synchronized(偏向锁,轻量级锁,重量级锁)

 

volatile

 

jdk提供的原子类

 

使用Lock(共享锁,排它锁)

 

 

 

 

posted @ 2019-02-11 20:49  曲阳阳  阅读(136)  评论(0编辑  收藏  举报