zoukankan      html  css  js  c++  java
  • java多线程基础

    1 基本概念 

          同步(synchronous)异步(asynchronous)

          并发(concurrency)并行(paralleism) 区别:并发是一个cup分时执行不同部分,并行是多个cpu线程同时执行。

          临界区 :公共资源(共享数据)。当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。导致竞态条件发生的代码区称作临界区。每个资源一次只能有一个线程用,一旦临界区资源被占用,其他线程想使用必须等待。建立临界区用sychronized或者lock。

          阻塞(blocking) 非阻塞(non-blocking):多线程之间相互影响。阻塞:一个线程抢占临界区后其他线程挂起等待。非阻塞允许多线程进入临界区,分为无障碍,无锁,无等待。

                 (1)无障碍 最弱的非阻塞调度,可自由出入临界区,宽进严出。如果无竞争,则操作;如果有竞争发现数据冲突,会重试新的数据直到无冲突,每次数据都可看作一个快照。

                   (2)   无锁  前提是无障碍,保证一个线程可以胜出。cas。

                 (3) 无等待  前提是无锁,至少一个能进能出,要求所有线程都必须在有限步内完成,也就是无饥饿。   

    死锁(deadlock) 饥饿(stavation) 活锁(livelock)

    2 java线程状态

            (1)启动 start方法。 java多线程调用方法有两种,一种重写run函数,一种传入runnable,为什么呢?thread类的start方法,调用的是run方法,run方法里,调用线程自身的一个runnable实例target的run方法,所以自己写线程,要么重写run,要么传入新的target实例。

            (2)终止  Thread.stop(),不推荐使用,释放当前所有锁,由于太暴力,有可能导致数据不一致。

            (3)中断  Thread.interrupt() 中断,isInterrupted() 判断是否中断。一般来说,从主线程调用中断函数,在线程中判断是否中断,如果中断执行相应操作如停止工作。中断相当于对线程发一个信号,修改线程标志位。

                     * 注意,使用线程sleep的时候,会抛出一个InterruptedException,是因为sleep的时候,无法响应interrupt中断,所以一旦中断标志位设置,就会在sleep中抛出异常,可以在catch中操作中断行为,方便立即响应。sleep()可以使低优先级的线程得到执行的机会,当然也可以让同优先级、高优先级的线程有执行的机会。例如有两个线程同时执行(没有synchronized)一个线程优先级为MAX_PRIORITY,另一个为MIN_PRIORITY,如果没有Sleep()方法,只有高优先级的线程执行完毕后,低优先级的线程才能够执行;但是高优先级的线程sleep(500)后,低优先级就有机会执行了。

            (4)挂起(suspend)和继续执行(resume)。挂起不会释放锁,容易死锁不推荐使用,例如在不同线程里想resume一个线程,但resume和suspend顺序不固定,可能永远suspend。

            (5)等待线程结束(join) 谦让(yeild)。join会等待线程结束,用法类似countDownLatch.wait(),传入时间则只等待固定时间。  join的本质是 while(isAlive()){wait(0)}。

                  yeild会让线程暂停,让出cpu资源,让同优先级线程一起竞争(cpu时间片),但不能指定多少时间,也不释放锁。

              * 守护线程  运维性质的工作,比如垃圾回收线程,jit线程。当一个java应用内只有守护线程时,虚拟机就会自然退出。setDaemon(ture) 可以把某线程设置为守护线程,但需要在start()之前设置。

              *线程优先级  线程上调用setPriority()设置优先级,优先级枚举例如Thread.MAX_PRIORITY,高优先级有更高概率抢资源,但不一定。

    3 线程同步操作

          (1)synchronized

                    对给定对象加锁,进入同步代码块前要获得给定对象的锁。

                   给实例方法加锁,相当于对当前实例加锁,进入同步代码块之前要获得当前实例的锁。

                   给静态方法加锁,给当前类加锁,进入同步代码块前要获得当前类的锁。 

          (2)wait notify  

                   持有了某个对象的锁,才能调用这个对象的wait,如sychronized(object){object.wait();},否则报异常,会使当前线程释放锁,同样是等待,给sleep相反;

                   同样,只有获得某个对象的锁,才能调用notify,notify唤醒的线程会重新争抢锁资源,如果调用notify的时候代码块不释放锁,那么唤醒的线程由于争抢不到资源还是无法执行。注意 notify只能随机唤醒一个等待的线程,nofityall可以唤醒所有等待线程。     

         (3)多线程内存模型

                原子性 指一个不可中断的操作,多线程也不会进行干扰。i++就不是原子操作

                有序性  java线程会对指令可能进行冲排序,不影响实际逻辑,可以节省cpu时钟周期。

                可见性 但一个线程修改了共享变量的值,其他线程是否能够立即知道这个修改。       

         *volatile 只是保证可见性,并不能保证原子性。使用volatile关键字会强制将修改的值立即写入主存;volatile会在变量上加一个lock前缀指令,相当于内存屏障,防止冲排序,lock前后内容不会混起来冲排序,lock之前内容必须是都执行完的,在对变量进行写操作的时候,会把结果立刻存入内存,并让所有的高速缓存(线程内存)的该变量副本无效。volatile防止重排序的功能实例如下 

    //线程1:
    context = loadContext();   //语句1
    inited = true;             //语句2

    //线程2:
    while(!inited ){
     sleep()
    }
    doSomethingwithconfig(context);
    有可能语句2会在语句1之前执行,那么久可能导致context还没被初始化,而线程2中就使用未初始化的context去进行操作,导致程序出错。
    这里如果用volatile关键字对inited变量进行修饰,就不会出现这种问题了,因为当执行到语句2时,必定能保证context已经初始化完毕。

    下图内存模型

           

         volatile不能保证原子性,多线程如果拿到同一个值同时修改,结果也可能相互覆盖。

      

          *happen-before规则

                程序顺序规则: 保证语义串行性,重排序与否不影响结果

                volatile规则:volatile变量写先发于读,保证可见性。

                锁规则:解锁(unlock)必然发生在随后加锁(lock)前。

                传递性:a先于b,b先于c,a先于c。

                线程start()优先于每一个动作;所有操作先于终结(join) 。

                线程中断(interrupt)先于被中断线程的代码。

                对象构造函数执行结束先于finalize()方法。

    4 关键字原理 

    每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

    1)如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

    2)如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.

    3)如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

          *Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。

          *wait方法的使用必须在同步的范围内,否则就会抛出IllegalMonitorStateException异常。调用wait方法后,线程是会释放对monitor对象的所有权。

      *sleep暂停期间一直持有monitor对象锁,其他线程不能进入。

  • 相关阅读:
    https://github.com/cykl/infoqscraper/
    C# 笔记
    json.org
    python html parse
    doxygen
    review board
    ruunlevel debian
    连接REDIS
    composer
    php需要注意的地方
  • 原文地址:https://www.cnblogs.com/lkdirk/p/6627192.html
Copyright © 2011-2022 走看看