• 多线程安全


    线程安全性问题

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

    线程所带来的风险

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

    性能问题

     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(共享锁,排它锁)

  • 相关阅读:
    服务器资源共享--IIS站点/虚拟目录中访问共享目录(UNC)
    sql reiserror 输出错误
    使用xib方式创建UITableViewCell,设置Label自动换行注意事项
    原生的UITableViewCell高度自适应,textLabel自动换行显示
    屏幕截取-2种模式
    NSDictionary初始化,使用@{}方法,插入nil时会报空指针异常
    Unicode解码、URL编码/解码
    解决UITableView数据没有充满屏幕时,显示多余的空白cell的问题
    UITableView的分割线不满屏的解决方法
    动态获取UIWebView的真正高度
  • 原文地址:https://www.cnblogs.com/quyangyang/p/10361492.html
Copyright © 2020-2023  润新知