java多线程技术知识汇总

一.Thread的生命周期

    Thread类是线程类,是创建多线程的基础。线程的生命周期有5个状态:新建(New),就绪(Runable),运行(Running),阻塞(Blocked)和死亡(Dead)。线程各状态之间相互转换,及线程的生命周期,如下图所示:

java多线程技术知识汇总_第1张图片

参考文章:https://www.cnblogs.com/fysola/p/6066290.html

在线程中,线程状态变化的方法有:start,wait,sleep,yield,join,notify, interrupt,stop等。其中interrupt和stop用来强制退出线程,造成很大风险不常用。下面介绍其他几个方法:

1.start()是Thread的对象方法。新建Thread实例后,线程的状态为New,当调用start方法后,线程的状态变为Runable,当cpu分配时间片给此线程时,此线程就开始运行,线程的状态为Running。

2.wait()也是Thread的对象方法。线程调用wait方法,此线程放弃执行权,放弃对象锁,把CPU资源让给其他线程,自己进入等待池中,并监控着cpu的使用状况,需要运行着的其他线程调用notify方法,才能把CPU让给等待池中的线程。当线程被唤醒后,获取对象锁,继续执行。另外,wait方法必须在synchronized块中执行,否则运行时会报IllegalMonitorStateException异常。

3.sleep()是Thread的类方法。调用此方法时,当前线程进入停滞状态,即阻塞了当前线程执行。让出CPU资源给其他线程。它不能让线程放弃对象锁,仅仅是等待一段时间,就相当于在此处休眠了,时间一到,进入Runable状态,等分配到CPU资源就继续执行。

4.yield()是Thread的类方法。yield是放弃的意思,是告诉cpu当前线程可以让其他线程占用自己分配的CPU资源,但是并没有指明多长时间,一般也就几个时间片。从yield()的定义中知道此方法是一个静态的原生方法;它告诉当前正在执行的线程,把运行机会让给线程池中拥有相同等级的线程;它仅能使一个线程从运行状态转到可运行状态,而不是阻塞状态;它不能保证当前正在运行的线程迅速转换为可运行状态。参考资料:http://www.importnew.com/14958.html

5.join()是Thread的对象方法。一个线程调用join方法,是告诉其他线程,要等我执行完毕,其他线程才可以执行。

6.notify()是Thread的对象方法。一个线程调用notify方法,是要告诉等待池中的线程,我让出CPU资源,并把其中一个等待池中的线程唤醒。若等待池中有多个线程在等待,使用nofity(),仅能唤醒其中一个线程,而其他线程可能永远无法唤醒。所以使用notifyAll()方法会比使用notify()更好,它会唤醒所有在等待的线程到Runable状态,确保每个线程都能被唤醒。

在Thread的这些方法中,使用的最多的是start()和sleep()。

二. volatile关键字

    volatile用来修饰需要在多线程中共享的变量,用来确保将变量的更新通知到其他线程。要想更深地了解volatile关键字的实现原理,就需要了解计算机的内存模型,特别是多核下的内存模型。

    cpu需要通过内存读取数据,且为了确保执行性能,每个cpu都有高速缓存。所以每个线程在读取一个变量值时,可能有多份缓存。当变量值发生变化时,其他线程读取的缓存值需要得到通知。这就是volatile的作用,确保每个cpu的缓存都得到通知,并获取最新值。

    由于编译器和CPU为了确保性能,所以需要对生成的机器码进行优化,但又要确保程序正确执行,编译器和CPU单线程可以确保单线程程序的正确性,但多线程就有些复杂。多线程模型需要解决三个问题:原子性问题,可见性问题,有序性问题。

    原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
    可见性:是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
    有序性:即程序执行的顺序按照代码的先后顺序执行。

    一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

    a.保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

    b.禁止进行指令重排序。

    在确保变量在多线程中的原子性,可见性和有序性,volatile关键字的使用场景为:

    a.对变量的写操作不依赖于当前值;

    b.该变量没有包含在具有其他变量的不变式中;

    其他更复杂的场景就需要多线程的其他机制来保障了。例如:synchronized,Lock和包java.util.concurrent.atomic内的原子性类等。

    参考资料:https://www.cnblogs.com/dolphin0520/p/3920373.html

                      https://www.cnblogs.com/zhengbin/p/5654805.html

    在延迟化初始化单例变量是也会用到volatile关键字。因为由于java的内存模型会重排代码顺序,双重检查锁并不能确保单例变量在多线程系统中只被初始化一次。若用volatile关键字修饰了单例变量,就能确保代码的有序性和可见性,也确保了单例变量仅被初始化一次。

    参考资料:http://www.cnblogs.com/xz816111/p/8470048.html

三.synchronize关键字

 

四.executor和task优于线程

    Thread是比较基础的线程管理类,使用起来比较复杂,想用得好更是不容易。java.util.concurrent包中提供了三种更高级的多线程管理工具:Executor Framework,Concurrent Collection(并发集合),以及Synchronizer(同步器),这三种工具能帮助我们更好地管理多线程程序。先说说Executor Framework,它是一个很灵活的基于接口的任务执行工具。

 

五.并发集合

 

六.同步器

    同步器是一些使线程能够等待另一个线程的对象,允许它们协调动作。最常用的同步器是CountDownLatch和Semaphore。还有不那么常用的CyclicBarrier和Exchanger。

    1.CountDownLatch(倒计数锁存器)是一次性的障碍,允许一个或多个线程等待一个或多个其他线程来做某些事情。CountDownLatch的唯一构造器带有一个int类型的参数,这个参数是允许所有在等待的线程被处理之前,必须在锁存器上调用countDown方法的次数。

 

你可能感兴趣的:(java)