泡妞的一个中心和两个基点

小说:泡妞的一个中心和两个基点作者:道侯纯陵更新时间:2019-05-19字数:71054

李?m最后也明白了李庆安的策略,他对李庆安的态度也由不满转为了佩服。

心理咨询,为什么要收费?

真武大步流星出了凌霄宝殿,殿上众人个个赞叹,果然玉帝圣明,能教众仙同心戮力。
史朝义却摆摆手笑道:“当然不是真的,其实我父亲都知道,杨贵妃肯定是在李庆安手中,但安禄山却不肯相信,这段时间我父亲一直在寻找,结果真在易州的乡村里找到了一个长得很像杨贵妃的女人,当然只是外貌相,她只是一个乡村愚妇,和贵妃是没得比,但我父亲一直在训练她,最近终于训练出一点模样了,我父亲便将她藏在太原府的曲阳县,这个消息你要找机会透露给安禄山。”

本来要被吞噬的天道崩灭居然硬生生的抗住了饕餮盛宴,可是饕餮盛宴号称饕餮最强神通可不是那么简单的,黑暗世界当中出现了一张无比巨大的黑暗饕餮之口,张口咬在了天道崩灭之上两股犹如大道显化一般的力量不断的碰撞之间居然陷入了一个僵局。

当线程安全:多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或协调,这个类都能表现出正确的行为,那么久称这个类是线程安全的。

在线程安全类中封装了必要的同步机制,因此客户端无需采取进一步的同步措施。

原子性


要么不执行,要么执行到底。原子性就是当某一个线程修改i的值的时候,从取出i到将新的i的值写给i之间不能有其他线程对i进行任何操作。也就是说保证某个线程对i的操作是原子性的,这样就可以避免数据脏读。 通过锁机制或者CAS(Compare And Set 需要硬件CPU的支持)操作可以保证操作的原子性。

当多个线程访问某个状态变量,并且其中有一个线程执行写入操作时,必须采用同步机制来协调这些线程对变量的访问。无状态对象一定是线程安全的。

  如果我们在无状态的对象中增加一个状态时,会出现什么情况呢?假设我们按照以下方式在servlet中增加一个"命中计数器"来管理请求数量:在servlet中增加一个long类型的域,每处理一个请求就在这个值上加1。

public class UnsafeCountingFactorizer implements Servlet {
     private long count = 0;

     public long getCount() {
            return count ;
     }

     @Override
     public void service(ServletRequest arg0, ServletResponse arg1)
                 throws ServletException, IOException {
            // do something
           count++;
     }
}

不幸的是,以上代码不是线程安全的,因为count++并非是原子操作,实际上,它包含了三个独立的操作:读取count的值,将值加1,然后将计算结果写入count。如果线程A读到count为10,马上线程B读到count也为10,线程A加1写入后为11,线程B由于已经读过count值为10,执行加1写入后依然为11,这样就丢失了一次计数。

        在 count++例子中线程不安全是因为 count++并非原子操作,我们可以使用原子类,确保确保操作是原子,这样这个类就是线程安全的了。

public class CountingFactorizer implements Servlet {
     private final AtomicLong count = new AtomicLong(0);

    public long getCount() {
          return count .get() ;
   }

    @Override
    public void service(ServletRequest arg0, ServletResponse arg1)
               throws ServletException, IOException {
          // do something
          count.incrementAndGet();
   }
}

       AtomicLong是java.util.concurrent.atomic包中的原子变量类,它能够实现原子的自增操作,这样就是线程安全的了。   同样,上述情况还会出现在 单例模式的懒加载过程中,当多个线程同时访问 getInstance()函数时。这篇文章中有讲解:实现优雅的单例模式

加锁机制


      线程在执行被synchronized修饰的代码块时,首先检查是否有其他线程持有该锁,如果有则阻塞等待,如果没有则持有该锁,并在执行完之后释放该锁。

      除了使用原子变量的方式外,我们也可以通过加锁的方式实现线程安全性。还是UnsafeCountingFactorizer,我们只要在它的service方法上增加synchronized关键字,那么它就是线程安全的了。当然在整个方法中加锁在这里是效率很低的,因为我们只需要保证count++操作的原子性,所以这里只对count++进行了加锁,代码如下:

public class UnsafeCountingFactorizer implements Servlet {
     private long count = 0;

     public long getCount() {
            return count ;
     }

     @Override
     public void service(ServletRequest arg0, ServletResponse arg1)
                 throws ServletException, IOException {
            // do something
           synchronized(this){
               count++;
          }
     }
}

Synchronized代码块使得一段程序的执行具有 原子性,即每个时刻只能有一个线程持有这个代码块,多个线程执行在执行时会互不干扰。

java 内存模型及 可见性


     的内存模型没有上面这么简单,在Java Memory Model中,Memory分为两类,main memory和working memory,main memory为所有线程共享,working memory中存放的是线程所需要的变量的拷贝(线程要对main memory中的内容进行操作的话,首先需要拷贝到自己的working memory,一般为了速度,working memory一般是在cpu的cache中的)。被volatile修饰的变量在被操作的时候不会产生working memory的拷贝,而是直接操作main memory,当然volatile虽然解决了变量的可见性问题,但没有解决变量操作的原子性的问题,这个还需要synchronized或者CAS相关操作配合进行。

每个线程内部都保有共享变量的副本,当一个线程更新了这个共享变量,另一个线程可能看的到,可能看不到,这就是可见性问题。

下面这段代码中 main 线程中 改变了 ready的值,当开启多个子线程时,子线程的值并不是马上就刷新为最新的ready的值(这里的中间刷新的时间间隔到底是多长,或者子线程的刷新机制,自己也不太清楚。当开启一个线程去执行时,ready值改变时就会立刻刷新,循环立刻就结束,但是当开启多个线程时,就会有一定的延迟)。

public class SelfTest {
    private  static boolean ready;
    private static int number;
    private static long time;

    public static class ReadThread extends Thread {
        public void run() {
            while(!ready ){
                System. out.println("*******  "+Thread.currentThread()+""+number);
                Thread. yield();
            }
            System. out.println(number+"   currentThread: "+Thread.currentThread());
        }
    }
    public static void main(String [] args) {
        time = System.currentTimeMillis();
        new ReadThread().start();
        new ReadThread().start();
        new ReadThread().start();
        new ReadThread().start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        number = 42;
        ready = true ;
        System.out.println("赋值时间:ready = true     ");
    }
}

上面这段代码的执行结果:可以看出赋值后,循环还是执行了几次。

image

此时如果把 ready的属性加上 volatile 结果便是如下的效果:

image

由此可见Volatile可以解决内存可见性的问题。

上面讲的加锁机制同样可以解决内存可见性的问题,加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步。

注:由于System.out.println的执行仍然需要时间,所以这面打印的顺序还是可能出现错乱。

参考:

http://www.mamicode.com/info-detail-245652.html

并发编程实战

http://www.cnblogs.com/NeilZhang/p/7979629.html

编辑:杜帝乙平

发布:2019-05-19 07:51:05

当前文章:http://cnsdbtzg.com/ask/question_22453.html

文字艺术优化(文字优化) 怀念谷歌 - 缘于深恶痛觉百度的唯利是图 裸露胸部就是下流吗? 如何修炼一个吸引妹子的领袖型人格? 小偷数秒内就能打开的防盗锁!你家的安全吗? 妈妈要重视,宝宝打呼噜,不是睡得香! 名正则言顺,言顺则运通,运通则事成 为什么说大部分人的勤奋是完全无效的

11369 37976 83546 79624 59436 77004 54516 89583 37047 39601 94298 27949 23850 51387 96949 36698 12651 90645 59572 23788

我要说两句: (0人参与)

发布