• Java基础知识盘点(三)- 线程篇


    创建线程的方式及实现

      一、继承Thread类创建线程类

        1、定义Thread的子类,并重写run方法,因为该方法的方法体就是代表了线程要完成的任务,因此run方法又叫做执行体。

        2、创建Thread子类的实例,即创建了线程对象。

        3、调用线程对象的start方法来启动该线程。

      二、通过Runnable接口来创建线程类

        1、定义Runnable接口的实现类,并重写run方法,跟Thread一样,该run方法又叫做执行体。

        2、创建实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的对象。

        3、调用线程对象的start方法来启动该线程。

      三、通过Callable和Future创建线程

        1、创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。

        2、创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。

        3、使用FutureTask对象作为Thread对象的target创建并启动新线程。

        4、调用FutureTask对象的get()方法来获得子线程执行结束后的返回值

      创建线程的三种方式的对比

        采用实现Runnable、Callable接口的方式创见多线程时

        优势是:

          线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。

          在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。

        劣势是:

          编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。

        使用继承Thread类的方式创建多线程时优势是:

          编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。

        劣势是:

          线程类已经继承了Thread类,所以不能再继承其他父类。

     

    sleep()、join()、yield()区别

      1、sleep()

        在指定毫秒数为单位,让当前正在执行的线程处于休眠状态,但此操作并不会释放对象锁。

      2、yield()

        它与sleep类型,也不会释放锁,但它没有参数,即yield()方法只是使当前线程重新回到可执行状态。该方法只能使同优先级或高优先级的线程得到执行机会,这也和sleep方法不同。 

      3、join()

        Thread的非静态方法join()让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作。

    sleep()和wait()的区别

      1、对于sleep()方法,我们首先要知道该方法是属于Thread类中的。而wait()方法,则是属于Object类中的

      2、sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态

      3、在调用sleep()方法的过程中,线程不会释放对象锁

      4、而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。

    CountDownLatch 原理

      CountDownLatch是同步工具类之一,可以指定一个计数器,在并发环境下由线程进行减1操作,当计数器变为0时,被await方法阻塞的线程将会被唤醒,实现线程间的同步。

    CyclicBarrier 原理

      字面意思是可循环(Cyclic)使用的屏障(Barrier)。它要做的事情是让一组线程到达一个屏障(同步点)时被阻塞,直到最后一个线程到达屏障时候,屏障才会开门。所有被屏障拦截的线程才会运行。

    说说Semaphore原理

      信号量,用来控制多线程同时访问特定资源时的线程数量,它通过协调合理的使用公共资源。线程通过acquire方法活动信号量的许可,当信号量没有可用的许可的时候,线程阻塞,直到有可用的线程为止。线程可以通过releace方法来释放它持有的信号量许可。

    说说 Exchanger 原理

      JDK1.5提供的工具套件,用来处理两个线程之间的数据交换。

    ThreadLocal 原理分析

      线程局部变量,并发的时候成员变量是不安全的,因为同时有多个线程在访问,我们需要满足这样一个条件:变量是同一个,但是每个线程都使用同一个初始值,也就是使用同一个变量的一个新的副本,当我们调用get方法的时候,其实每个当前线程中都有一个ThreadLocal。每次获取或者设置都是对该ThreadLocal进行的操作,是与其他线程分开的。

    线程池的几种方式

      1、newFixedThreadPool(int nThreads):创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程的规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程

      2、newCachedThreadPool():创建一个可缓存的线程池,如果线程池超过了处理需求,将自动回收线程池,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制

      3、newSingleThreadExecutor():单线程的Executor,它创建单个工作线程来执行任务,如果这个任务异常结束,会创建一个新的来替代它,它的特点是能确保依照任务在队列中的顺序来串行执行

      4、newScheduledThreadPool(int corePoolSize):创建一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。

    线程的生命周期

      新建(new)、就绪(Runnable)、运行(Running)、阻塞(Blockde)、死亡(Dead)

      新建(new Thread):当创建Thread类的实例对象时,此线程进入新建状态(未被启动)

      就绪(Runnable):线程被启动,正在序列队列中等待被分配给CPU资源

      运行(Running):线程获得了CPU资源,正在执行任务(run()方法),运行中除非该线程自动放弃CPU资源或有更高优先级的线程进入,否则它将一直执行下去到任务结束。

      死亡(dead):该线程执行完毕后或被其他线程杀死,线程就进入到了死亡状态,此时线程不可能再进入等待执行状态

      阻塞(Blocked):由于某些原因导致该线程让出CPU并暂停自己的执行。

    锁机制

    说说线程安全问题

    线程安全是指要控制多个线程对某个资源的有序访问或修改,而在这些线程之间没有产生冲突。

    线程安全一般体现在两个方面:

      1、多个Thread对同一个实例的访问(read和modify)不会相互干扰,它主要体现在关键字synchronized上。如ArrayList和Vector,HashMap和HashTable(后者每个方法前都有synchronized关键字)

      2、每个线程都有自己的字段,而不会在多个线程之间共享。它主要体现在java.lang.ThreadLocal类,而没有java关键字支持,如static、transient那样。  

    悲观锁 乐观锁

    悲观锁:总是假设最坏的情况,每次取数据时都认为其他线程会修改,在Java中,synchronized的思想也是悲观锁。

    乐观锁:总是认为不会产生并发问题,每次取数据的时候总是认为不会被其他线程修改,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现

      1、version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功

      2、CAS操作方式:即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。  

  • 相关阅读:
    react Table key值使用方式
    C# EF查询不同分组的第一条数据
    C# MVC PDFJS URL传参方式+文件流方式在线展示文档
    docker nginx配置写错,启动不了容器的解决方案
    网络编程之BIO和NIO
    网络编程之BIO和NIO
    IT职场心得感想
    我们希望能与各位快乐拼博的站长们一路前行
    UART, SPI详解
    创业精神
  • 原文地址:https://www.cnblogs.com/cristin/p/9655830.html
Copyright © 2020-2023  润新知