精华内容
下载资源
问答
  • 展开全部Java线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多636f70793231313335323631343130323136353331333363373731线程。其中前两种方式...

    展开全部

    Java多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多636f70793231313335323631343130323136353331333363373731线程。其中前两种方式线程执行完后都没有返回值,只有最后一种是带返回值的。

    1、继承Thread类实现多线程

    继承Thread类的方法尽管被我列为一种多线程实现方式,但Thread本质上也是实现了Runnable接口的一个实例,它代表一个线程的实例,并且,启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。这种方式实现多线程很简单,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己定义的run()方法。例如:

    7b58d753c6c16d28941e2e49a5fc3fb7.png

    在合适的地方启动线程如下:

    4f5780d6a8a766ed0ec0891f18a79982.png

    2、实现Runnable接口方式实现多线程

    如果自己的类已经extends另一个类,就无法直接extends Thread,此时,必须实现一个Runnable接口,如下:

    71d5acf41ed018145a0a1467b71550a1.png

    为了启动MyThread,需要首先实例化一个Thread,并传入自己的MyThread实例:

    5e9f63a62e5f92ccaa5937fc9d313ed3.png

    事实上,当传入一个Runnable target参数给Thread后,Thread的run()方法就会调用target.run(),参考JDK源代码:

    4c492ca5de3ee0623648c99cceda6c86.png

    3、使用ExecutorService、Callable、Future实现有返回结果的多线程

    ExecutorService、Callable、Future这个对象实际上都是属于Executor框架中的功能类。想要详细了解Executor框架的可以访问http://www.javaeye.com/topic/366591 ,这里面对该框架做了很详细的解释。返回结果的线程是在JDK1.5中引入的新特征,确实很实用,有了这种特征我就不需要再为了得到返回值而大费周折了,而且即便实现了也可能漏洞百出。

    可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了。下面提供了一个完整的有返回结果的多线程测试例子,在JDK1.5下验证过没问题可以直接使用。代码如下:

    b63337c4f0d8689d3f36b421b7666333.png

    0a77251c25e111098e089bebc1be5ce7.png

    代码说明:

    上述代码中Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。

    public static ExecutorService newFixedThreadPool(int nThreads)

    创建固定数目线程的线程池。

    public static ExecutorService newCachedThreadPool()

    创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。

    public static ExecutorService newSingleThreadExecutor()

    创建一个单线程化的Executor。

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

    创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

    总结:ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

    展开全文
  • 目录:缓存线程安全问题的本质出现线程安全的问题本质是由于:安全主内存和工做内存数据不一致性以及编译器重排序致使。服务器因此理解上述两个问题的核心,对认知多线程的问题则具备很高的意义;多线程简单理解CPU...

    目录:缓存

    线程安全问题的本质

    出现线程安全的问题本质是由于:安全

    主内存和工做内存数据不一致性以及编译器重排序致使。服务器

    因此理解上述两个问题的核心,对认知多线程的问题则具备很高的意义;多线程

    简单理解CPU

    CPU除了控制器、运算器等器件还有一个重要的部件就是寄存器。其中寄存器的做用就是进行数据的临时存储。并发

    CPU的运算速度是很是快的,为了性能CPU在内部开辟一小块临时存储区域,并在进行运算时先将数据从内存复制到这一小块临时存储区域中,运算时就在这一小快临时存储区域内进行。咱们称这一小块临时存储区域为寄存器。jvm

    CPU读取指令是往内存里面去读取的,读一条指令放到CPU中,CPU去执行,对内存的读取速度比较慢,因此从内存读取的速度去决定了这个CPU的执行速度的。因此不管咱们的CPU怎么去升级,可是若是这方面速度没有解决的话,其的性能也不会获得多大的提高。函数

    为了弥补这个缺陷,因此添加了高速缓存的机制,如ARM A11的处理器,它的1级缓存中的容量是64KB,2级缓存中的容量是8M,

    经过增长cpu高速缓存的机制,以此弥补服务器内存读写速度的效率问题;工具

    JVM虚拟机类比于操做系统

    JVM虚拟计算机平台就相似于一个操做系统的角色,因此在具体实现上JVM虚拟机也的确是借鉴了不少操做系统的特色;性能

    JAVA中线程的工做空间(working memory)就是CPU的寄存器和高速缓存的抽象描述,cpu在计算的时候,并不老是从内存读取数据,它的数据读取顺序优先级 是:寄存器-高速缓存-内存;

    而在JAVA的内存模型中也是同等的,Java内存模型中规定了全部的变量都存储在主内存中,每条线程还有本身的工做内存(相似于CPU的高速缓存),线程的工做内存中保存了该线程使用到的变量到主内存副本拷贝,线程对变量的全部操做(读取、赋值)都必须在工做内存中进行,而不能直接读写主内存中的变量,操做完成后再将变量写回主内存。不一样线程之间没法直接访问对方工做内存中的变量,线程间变量值的传递均须要在主内存来完成。基本关系以下图:

    750d038e2fe6679baaf8f134e73b1381.png

    注意:这里的Java内存模型,主内存、工做内存与Java内存区域模型的Java堆、栈、方法区不是同一层次内存划分,这二者基本上没有关系。

    重排序

    在执行程序时,为了提升性能,编译器和处理器经常会对指令进行重排序。通常重排序能够分为以下三种:

    b2afee9435853d6b84be9d3bca63f948.png

    举例以下:

    public class Singleton {

    public static Singleton singleton;

    /**

    * 构造函数私有,禁止外部实例化

    */

    private Singleton() {};

    public static Singleton getInstance() {

    if (singleton == null) {

    singleton = new Singleton();

    }

    return singleton;

    }

    }

    如上,一个简单的单例模式,按照对象的构造过程,实例化一个对象一、能够分为三个步骤(指令):

    一、 分配内存空间。

    二、 初始化对象。

    三、 将内存空间的地址赋值给对应的引用。

    可是因为操做系统能够对指令进行重排序,因此上面的过程也可能变为以下的过程:

    一、 分配内存空间。

    二、 将内存空间的地址赋值给对应的引用。

    三、 初始化对象 。

    因此,若是出现并发访问getInstance()方法时,则可能会出现,线程二判断singleton是否为空,此时因为当前该singleton已经分配了内存地址,但其实并无初始化对象,则会致使return 一个未初始化的对象引用暴露出来,以此可能会出现一些不可预料的代码异常;

    固然,指令重排序的问题并不是每次都会进行,在某些特殊的场景下,编译器和处理器是不会进行重排序的,但上述的举例场景则是大几率会出现指令重排序问题(关于指令重排序的概念后续给出详细的地址)

    汇总

    因此,如上可知,多线程在执行过程当中,数据的不可见性,原子性,以及重排序所引发的指令有序性 三个问题基本是多线程并发问题的三个重要特性,也就是咱们常说的:

    并发的三大特性:原子性,有序性,可见性;

    原子性:代码操做是不是原子操做(如:i++ 看似一个代码片断,实际的执行中将会分为三步执行,则必然是非原子化的操做,在多线程的场景中则会出现异常)

    有序性:CPU执行代码指令时的有序性;

    可见性:因为工做线程的内存与主内存的数据不一样步,而致使的数据可见性问题;

    一些解释

    可是,问题就真的有那么复杂吗?若是按照上面所说的问题,i++是非原子操做,就会出现并发异常的问题,new Object() 就会出现重排序的并发问题,那么Java开发还能作吗。。我随便写个方法代码,岂不是就会出现并发问题?可是为何我开发了这么久的代码,也没有出现过方法并发致使的异常问题啊?

    烧的麻袋;

    这里就要说明另一个问题,JVM的线程栈,JVM线程栈中是线程独有的内存空间(如:程序计数器以线程栈帧)而线程栈帧中的局部变量表则用来存储当前所执行方法的基本数据类型(包含 reference, returnAddress等),因此当方法在被线程执行的过程当中,相关的对象引用信息,以及基本类型的数据都是线程独有的,并不会出现多个线程访问时的并发问题,也就是简单来讲:一个方法内的变量定义以及方法内的业务代码,是不会出现并发问题的。多个线程并不会共享一个方法内的变量数据,而是每一个方法内的定义都属于当前该执行线程的独有栈空间中。(因此经过Java线程栈的这一独特特性天然当中则为咱们省了不少事项;)

    可是因为咱们的线程的数据操做不可能每次都去访问主存中的数据,对于线程所使用到的变量须要copy至线程内存中以增长咱们的执行速度,因此就引出了咱们上述所提到的并发问题的本质问题,线程工做空间和主内存的数据不一样步而致使的数据共享时的可见性问题;

    如:此时定义一个简单的类

    class Person{

    int a = 1;

    int b = 2;

    public void change() {

    a = 3;

    b = a;

    }

    public void print() {

    String result = "b=" + b + ";a=" + a;

    System.out.println(result);

    }

    public static void main(String[] args) {

    while (true) {

    final Person test = new Person();

    new Thread(() -> {

    Thread.sleep(10);

    test.change();

    }).start();

    new Thread(() -> {

    Thread.sleep(10);

    test.print();

    }).start();

    }

    }

    }

    如上,假设此时多个线程同时访问change()以及print() 方法,则可能会出现print所输出的结果是:b=2;a=1或者b=3;a=3;这两种都是正常现象,但还有多是会输出结果是:b=2;a=3以及b=3;a=1;

    Person类所定义的变量a和b,按照JVM内存区域划分,在对象实例化后则都是存储到数据堆中;

    按照咱们上述关于线程工做内存的解释来看,此时线程在执行change()方法和print()方法时,因为两个方法都有关于外部变量的引用,因此须要copy主内存中的这两个变量副本到对应的线程工做内存中进行操做,执行完之后再同步至主内存中。

    此时在A线程执行完change()方法后,a=3,b=3;但此时a=3在执行完成后尚未同步到主内存,但b=3此时已经提供至主内存了,那么此时B线程执行print()数据输出后,则获得的是结果是:b=3;a=1;同理也能够获得b=2;a=3的可能性结果;因此此处则因为线程共享变量的可见性问题,而致使了上述的问题;

    正是因为存在上述所提到的线程并发所可能引发的种种问题,因此JDK则也有了后续的一系列多线程玩法:ThreadLocal,CountDownLatch,ReentrantLock,Unsafe,synchronized,volatile,Executor,Future 这些供开发者在开发程序时用来对多线程保驾护航的助手类,以及JDK已经自身开发好的支持线程安全的一些工具类,StringBuffer,CopyOnWriteArrayList, ConcurrentHashMap,AtomicInteger等,供开发者开箱即用;后续针对这些JDK自身所提供的一些类的玩法会作进一步说明,顺便系统整理下脑中的信息,造成有效的知识结构;End;

    参考连接

    写到这里可能你依然会对线程工做内存和主内存的同步机制比较感兴趣,则能够参考这里:

    若是对上述所提到的线程栈的局部变量表等概念依然不是很清晰,则能够参考这里:

    展开全文
  • 一、Java中断的现象首先,看看Thread类里的几个方法:public static booleaninterrupted测试当前线程是否已经中断。线程的中断状态由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第...

    Java的中断是一种协作机制。也就是说调用线程对象的interrupt方法并不一定就中断了正在运行的线程,它只是要求线程自己在合适的时机中断自己。

    一、Java中断的现象

    首先,看看Thread类里的几个方法:

    public static booleaninterrupted

    测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。

    boolean isInterrupted()

    测试线程是否已经中断。线程的中断状态 不受该方法的影响。

    public voidinterrupt()

    中断线程。

    上面列出了与中断有关的几个方法及其行为,可以看到interrupt是中断线程。如果不了解Java的中断机制,这样的一种解释极容易造成误解,认为调用了线程的interrupt方法就一定会中断线程。

    其实,Java的中断是一种协作机制。也就是说调用线程对象的interrupt方法并不一定就中断了正在运行的线程,它只是要求线程自己在合适的时机中断自己。每个线程都有一个boolean的中断状态(不一定就是对象的属性,事实上,该状态也确实不是Thread的字段),interrupt方法仅仅只是将该状态置为true 。

    经运行可以发现,程序抛出异常停止了,run方法里的后两条打印语句没有执行。那么,区别在哪里?

    一般说来,如果一个方法声明抛出InterruptedException,表示该方法是可中断的(没有在方法中处理中断却也声明抛出 InterruptedException的除外),也就是说可中断方法会对interrupt调用做出响应(例如sleep响应interrupt的操 作包括清除中断状态,抛出InterruptedException)。

    如果interrupt调用是在可中断方法之前调用,可中断方法一定会处理中断,像上面的例子,interrupt方法极可能在run未进入sleep的 时候就调用了,但sleep检测到中断,就会处理该中断。如果在可中断方法正在执行中的时候调用interrupt,会怎么样呢?这就要看可中断方法处理 中断的时机了,只要可中断方法能检测到中断状态为true,就应该处理中断。让我们为开头的那段代码加上中断处理。

    那么自定义的可中断方法该如何处理中断呢?那就是在适合处理中断的地方检测线程中断状态并处理。

    这段代码中检测中断用了Thread的静态方法interrupted,它将中断状态置为false,并将之前的状态返回,而isInterrupted只是检测中断,并不改变中断状态。一般来说,处理过了中断请求,应该将其状态置为false。但具体还要看实际情形。

    二、Java中断的本质

    在历史上,Java试图提供过抢占式限制中断,但问题多多,例如已被废弃的 Thread.stop、Thread.suspend和 Thread.resume等。另一方面,出于Java应用代码的健壮性的考虑,降低了编程门槛,减少不清楚底层机制的程序员无意破坏系统的概率。

    如今,Java的线程调度不提供抢占式中断,而采用协作式的中断。其实,协作式的中断,原理很简单,就是轮询某个表示中断的标记,我们在任何普通代码的中都可以实现。

    通常情况下,调用线程的interrupt方法,并不能立即引发中断,只是设置了JVM内部的中断标记。因此,通过检查中断标记,应用程序可以做一些特殊操作,也可以完全忽略中断。

    你可能想,如果JVM只提供了这种简陋的中断机制,那和应用程序自己定义中断变量并轮询的方法相比,基本也没有什么优势。

    JVM内部中断变量的主要优势,就是对于某些情况,提供了模拟自动“中断陷入”的机制。

    在执行涉及线程调度的阻塞调用时(例如wait、sleep和join),如果发生中断,被阻塞线程会“尽可能快的”抛出InterruptedException。因此,我们就可以用下面的代码框架来处理线程阻塞中断:

    所谓“尽可能快”,我猜测JVM就是在线程调度调度的间隙检查中断变量,速度取决于JVM的实现和硬件的性能。

    三、一些不会抛出 InterruptedException 的线程阻塞操作

    然而,对于某些线程阻塞操作,JVM并不会自动抛出InterruptedException异常。例如,某些I/O操作和内部锁操作。对于这类操作,可以用其他方式模拟中断:

    1)java.io中的异步socket I/O

    读写socket的时候,InputStream和OutputStream的read和write方法会阻塞等待,但不会响应java中断。不过,调用Socket的close方法后,被阻塞线程会抛出SocketException异常。

    2)利用Selector实现的异步I/O

    如果线程被阻塞于Selector.select(在java.nio.channels中),调用wakeup方法会引起ClosedSelectorException异常。

    3)锁获取

    如果线程在等待获取一个内部锁,我们将无法中断它。但是,利用Lock类的lockInterruptibly方法,我们可以在等待锁的同时,提供中断能力。

    四、两条编程原则

    另外,在任务与线程分离的框架中,任务通常并不知道自身会被哪个线程调用,也就不知道调用线程处理中断的策略。所以,在任务设置了线程中断标记后,并不能确保任务会被取消。因此,有以下两条编程原则:

    1)除非你知道线程的中断策略,否则不应该中断它。

    这条原则告诉我们,不应该直接调用Executer之类框架中线程的interrupt方法,应该利用诸如Future.cancel的方法来取消任务。

    2)任务代码不该猜测中断对执行线程的含义。

    这条原则告诉我们,一般代码遇在到InterruptedException异常时,不应该将其捕获后“吞掉”,而应该继续向上层代码抛出。

    总之,Java中的非抢占式中断机制,要求我们必须改变传统的抢占式中断思路,在理解其本质的基础上,采用相应的原则和模式来编程。

    interrupt() 与 cancel()的区别

    两者实际上都是中断线程,但是后者更安全、有条理和高效,其原因跟推荐使用Executor而不直接使用Thread类是一致的。所以结合上面讲到的原则,我们应尽量采用cancel()方法,调用线程管理器ExecutorService接口的submit(Runnabletask) 方法会返回一个Future>对象,然后调用Future.cancel()的方法来取消任务,并返回一个boolean值。

    Object类的wait方法的API:

    http://bbs.csdn.net/topics/390253732

    抛出:

    IllegalMonitorStateException - 如果当前的线程不是此对象监视器的所有者。

    InterruptedException - 如果在当前线程等待通知之前或者正在等待通知时,另一个线程中断了当前线程。在抛出此异常时,当前线程的中断状态 被清除。

    wait

    public final void wait()

    throws InterruptedException在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。

    当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。

    对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用:

    synchronized (obj) {

    while ()

    obj.wait();

    ... // Perform action appropriate to condition

    }

    此方法只应由作为此对象监视器的所有者的线程来调用。有关线程能够成为监视器所有者的方法的描述,请参阅 notify 方法。

    抛出:

    IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。

    InterruptedException - 如果在当前线程等待通知之前或者正在等待通知时,任何线程中断了当前线程。在抛出此异常时,当前线程的中断状态 被清除。

    http://my.oschina.net/bayuanqian/blog/160491

    展开全文
  • Java线程间通信的方式

    2021-03-01 07:59:24
    Java线程间通信的方式线程间的通信方式①同步这里讲的同步是指多个线程通过 synchronized 关键字这种方式来实现线程间的通信。参考示例:[](javascript:void(0)????public class MyObject {synchronized public void...

    Java线程间通信的方式

    线程间的通信方式

    ①同步

    这里讲的同步是指多个线程通过 synchronized 关键字这种方式来实现线程间的通信。

    参考示例:

    [

    48304ba5e6f9fe08f3fa1abda7d326ab.png](javascript:void(0)😉

    public class MyObject {

    synchronized public void methodA() {

    //do something....

    }

    synchronized public void methodB() {

    //do some other thing

    }

    }

    public class ThreadA extends Thread {

    private MyObject object;

    //省略构造方法

    @Override

    public void run() {

    super.run();

    object.methodA();

    }

    }

    public class ThreadB extends Thread {

    private MyObject object;

    //省略构造方法

    @Override

    public void run() {

    super.run();

    object.methodB();

    }

    }

    public class Run {

    public static void main(String[] args) {

    MyObject object = new MyObject();

    //线程A与线程B 持有的是同一个对象:object

    ThreadA a = new ThreadA(object);

    ThreadB b = new ThreadB(object);

    a.start();

    b.start();

    }

    }

    [

    48304ba5e6f9fe08f3fa1abda7d326ab.png](javascript:void(0)😉

    由于线程 A 和线程 B 持有同一个 MyObject 类的对象 object,尽管这两个线程需要调用不同的方法,但是它们是同步执行的,比如:线程 B 需要等待线程 A 执行完了 methodA() 方法之后,它才能执行 methodB() 方法。这样,线程 A 和线程 B 就实现了 通信。

    这种方式,本质上就是 “共享内存” 式的通信。多个线程需要访问同一个共享变量,谁拿到了锁(获得了访问权限),谁就可以执行。

    ②while 轮询的方式

    代码如下:

    [

    48304ba5e6f9fe08f3fa1abda7d326ab.png](javascript:void(0)😉

    1 import java.util.ArrayList;

    2 import java.util.List;

    4 public class MyList {

    6 private List list = new ArrayList();

    7 public void add() {

    8 list.add("elements");

    9 }

    10 public int size() {

    11 return list.size();

    12 }

    13 }

    15 import mylist.MyList;

    17 public class ThreadA extends Thread {

    19 private MyList list;

    21 public ThreadA(MyList list) {

    22 super();

    23 this.list = list;

    24 }

    26 @Override

    27 public void run() {

    28 try {

    29 for (int i = 0; i < 10; i++) {

    30 list.add();

    31 System.out.println("添加了" + (i + 1) + "个元素");

    32 Thread.sleep(1000);

    33 }

    34 } catch (InterruptedException e) {

    35 e.printStackTrace();

    36 }

    37 }

    38 }

    40 import mylist.MyList;

    42 public class ThreadB extends Thread {

    44 private MyList list;

    46 public ThreadB(MyList list) {

    47 super();

    48 this.list = list;

    49 }

    51 @Override

    52 public void run() {

    53 try {

    54 while (true) {

    55 if (list.size() == 5) {

    56 System.out.println("==5, 线程b准备退出了");

    57 throw new InterruptedException();

    58 }

    59 }

    60 } catch (InterruptedException e) {

    61 e.printStackTrace();

    62 }

    63 }

    64 }

    66 import mylist.MyList;

    67 import extthread.ThreadA;

    68 import extthread.ThreadB;

    70 public class Test {

    72 public static void main(String[] args) {

    73 MyList service = new MyList();

    75 ThreadA a = new ThreadA(service);

    76 a.setName("A");

    77 a.start();

    79 ThreadB b = new ThreadB(service);

    80 b.setName("B");

    81 b.start();

    82 }

    83 }

    [

    48304ba5e6f9fe08f3fa1abda7d326ab.png](javascript:void(0)😉

    在这种方式下,线程 A 不断地改变条件,线程 ThreadB 不停地通过 while 语句检测这个条件 (list.size()==5) 是否成立 ,从而实现了线程间的通信。但是这种方式会浪费 CPU 资源。之所以说它浪费资源,是因为 JVM 调度器将 CPU 交给线程 B 执行时,它没做啥 “有用” 的工作,只是在不断地测试 某个条件是否成立。就类似于现实生活中,某个人一直看着手机屏幕是否有电话来了,而不是: 在干别的事情,当有电话来时,响铃通知 TA 电话来了。关于线程的轮询的影响,可参考:JAVA 多线程之当一个线程在执行死循环时会影响另外一个线程吗?

    这种方式还存在另外一个问题:

    轮询的条件的可见性问题,关于内存可见性问题,可参考:JAVA 多线程之 volatile 与 synchronized 的比较中的第一点 “一,volatile 关键字的可见性”

    线程都是先把变量读取到本地线程栈空间,然后再去再去修改的本地变量。因此,如果线程 B 每次都在取本地的 条件变量,那么尽管另外一个线程已经改变了轮询的条件,它也察觉不到,这样也会造成死循环。

    ③wait/notify 机制

    代码如下:

    [

    48304ba5e6f9fe08f3fa1abda7d326ab.png](javascript:void(0)😉

    1 import java.util.ArrayList;

    2 import java.util.List;

    4 public class MyList {

    6 private static List list = new ArrayList();

    8 public static void add() {

    9 list.add("anyString");

    10 }

    12 public static int size() {

    13 return list.size();

    14 }

    15 }

    18 public class ThreadA extends Thread {

    20 private Object lock;

    22 public ThreadA(Object lock) {

    23 super();

    24 this.lock = lock;

    25 }

    27 @Override

    28 public void run() {

    29 try {

    30 synchronized (lock) {

    31 if (MyList.size() != 5) {

    32 System.out.println("wait begin "

    33 + System.currentTimeMillis());

    34 lock.wait();

    35 System.out.println("wait end "

    36 + System.currentTimeMillis());

    37 }

    38 }

    39 } catch (InterruptedException e) {

    40 e.printStackTrace();

    41 }

    42 }

    43 }

    46 public class ThreadB extends Thread {

    47 private Object lock;

    49 public ThreadB(Object lock) {

    50 super();

    51 this.lock = lock;

    52 }

    54 @Override

    55 public void run() {

    56 try {

    57 synchronized (lock) {

    58 for (int i = 0; i < 10; i++) {

    59 MyList.add();

    60 if (MyList.size() == 5) {

    61 lock.notify();

    62 System.out.println("已经发出了通知");

    63 }

    64 System.out.println("添加了" + (i + 1) + "个元素!");

    65 Thread.sleep(1000);

    66 }

    67 }

    68 } catch (InterruptedException e) {

    69 e.printStackTrace();

    70 }

    71 }

    72 }

    74 public class Run {

    76 public static void main(String[] args) {

    78 try {

    79 Object lock = new Object();

    81 ThreadA a = new ThreadA(lock);

    82 a.start();

    84 Thread.sleep(50);

    86 ThreadB b = new ThreadB(lock);

    87 b.start();

    88 } catch (InterruptedException e) {

    89 e.printStackTrace();

    90 }

    91 }

    92 }

    [

    48304ba5e6f9fe08f3fa1abda7d326ab.png](javascript:void(0)😉

    线程 A 要等待某个条件满足时 (list.size()==5),才执行操作。线程 B 则向 list 中添加元素,改变 list 的 size。

    A,B 之间如何通信的呢?也就是说,线程 A 如何知道 list.size() 已经为 5 了呢?

    这里用到了 Object 类的 wait() 和 notify() 方法。

    当条件未满足时 (list.size() !=5),线程 A 调用 wait() 放弃 CPU,并进入阻塞状态。--- 不像②while 轮询那样占用 CPU

    当条件满足时,线程 B 调用 notify() 通知 线程 A,所谓通知线程 A,就是唤醒线程 A,并让它进入可运行状态。

    这种方式的一个好处就是 CPU 的利用率提高了。

    但是也有一些缺点:比如,线程 B 先执行,一下子添加了 5 个元素并调用了 notify() 发送了通知,而此时线程 A 还执行;当线程 A 执行并调用 wait() 时,那它永远就不可能被唤醒了。因为,线程 B 已经发了通知了,以后不再发通知了。这说明:通知过早,会打乱程序的执行逻辑。

    ④管道通信就是使用 java.io.PipedInputStream 和 java.io.PipedOutputStream 进行通信具体就不介绍了。

    分布式系统中说的两种通信机制:共享内存机制和消息通信机制。感觉前面的①中的 synchronized 关键字和②中的 while 轮询 “属于” 共享内存机制,由于是轮询的条件使用了 volatile 关键字修饰时,这就表示它们通过判断这个 “共享的条件变量 “是否改变了,来实现进程间的交流。

    而管道通信,更像消息传递机制,也就是说:通过管道,将一个线程中的消息发送给另一个。

    关于 wait/notify 更多内容,可参考:JAVA 多线程之 wait/notify

    展开全文
  • Java线程本质

    2021-02-12 19:42:44
    java当中的线程和操作系统的线程什么关系?关于操作系统的线程linux操作系统的线程控制原语int pthread create(pthread t *thread, const pthread attr t *attr,void *(*start_routine) (void *), void *arg);可以...
  • 线程安全的本质体现在两个方面,A变量安全:多线程同时运行一段代码B线程同步:一个线程还没执行完,另一个线程又进来接着执行。看个简单的例子。Java代码public class ThreadSafe implements java。lang。...
  • 这个问题是安琪拉之前面试被问到的一个问题,正好顺着上一篇文章介绍...面试官:Java线程用过的吧? 我:用过。 面试官:那你给我讲讲Java线程和操作系统的? 我:啊!!! 剧情不应该这样的啊,开场不应该先是 sync.
  • Java线程堆栈

    2021-03-16 20:20:40
    线程堆栈信息以及解决的问题1、线程堆栈的信息都包含:线程的名字,ID,线程的数量等。线程的运行状态,锁的状态(锁被哪个线程持有,哪个线程再等待锁等)。调用堆栈(函数的调用层次关系)。调用堆栈包含完整的类名,...
  • 线程(Thread)是并发编程的基础,也是程序执行的最小单元,它依托进程而存在。一个进程中可以包含多个线程,多线程可以共享一块内存空间和一组系统资源,因此线程之间的切换更加节省资源、更加轻量化,也因此被称为轻...
  • Java中出现线程中断的原因有哪些发布时间:2021-02-19 15:31:06来源:亿速云阅读:59作者:Leah这篇文章将为大家详细讲解有关Java中出现线程中断的原因有哪些,文章内容质量较高,因此小编分享给大家做个参考,希望...
  • java中创建线程的三种方法是什么?用Java创建线程的三种方法是什么?,在java中有三种创建线程的方法:1。继承线程类以创建线程;2.实现Runnable接口创建线程;3.使用可调用和未来创建线程Java使用Thread类代表...
  • Java 采用 thread-per-task 的线程模型,即一个任务(一段代码)对应一个 Java 线程(thread),而一个 Java 线程对应一个操作系统线程,所以了解一些操作系统进程的管理知识可以更好的了解 Java 线程,下面以 Liunx 为...
  • linux 查询java线程

    2021-05-13 17:10:49
    linux 查询java线程数[2021-02-04 07:46:01]简介:php去除nbsp的方法:首先创建一个PHP代码示例文件;然后通过“preg_replace("/(\s|\&nbsp\;| |\xc2\xa0)/", " ", strip_tags($val));”方法去除所有nbsp即可。...
  • java中创建线程的三种方法是:1、继承Thread类创建线程;2、实现Runnable接口创建线程;3、使用Callable和Future创建线程Java使用Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例。Java可以用三...
  • Java线程

    2021-02-28 14:03:03
    静态方法和普通方法同时加上synchronized有什么区别?A. 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它...
  • JAVA线程本质分析

    2021-03-22 14:09:13
    线程Java开发中的重中之重,其重要性和难度,可见一斑。掌握并精通多线程开发,是每一个程序员的必修之课。哪怕中间的过程很痛苦,只要坚持了,并最终豁然开朗了,都是一种升华。多线程的优化:合理利用CPU,...
  • JAVA线程的问题以及处理【转】

    千次阅读 2021-03-13 03:30:51
    http://www.cnblogs.com/springcsc/archive/2009/12/03/1616394.html12.4多线程问题及处理多线程编程为程序开发带来了很多的方便,但是也带来了一些问题,这些...在多线程编程中,这种会被多个线程同时访问的资源叫...
  • 点击上方“朱小厮的博客”,选择“设为星标”后台回复"书",获取后台回复“k8s”,可领取k8s资料title: 面试官问:为什么 Java 线程没有Running状态?我懵...
  • Linux从内核2.6开始使用NPTL (Native POSIX Thread Library)支持,但这时线程本质上仍是轻量级进程(LWP)。Java里的线程是由JVM来管理的,它如何对应到操做系统的线程是由JVM的实现来肯定的。Linux 2.6上的HotSpot...
  • 本文主要接着前面多线程的两篇文章总结Java多...一个典型的Java线程安全例子1 public class ThreadTest {23 public static void main(String[] args) {4 Account account = new Account("123456", 1000);5 DrawMoney...
  • 我懵了 —— 转 芋道源码什么是 RUNNABLE?与传统的ready状态的区别与传统的running状态的区别当I/O阻塞时如何看待RUNNABLE状态?Java虚拟机层面所暴露给我们的状态,与操作系统底层的线程状态是两个不同层面的事。...
  • 清楚的理解和认知线程状态是java多线程的基础,多线程本质上其实就是管理多个线程的状态,以期在保证线程安全的情况下获得最佳的运行效率(发挥cpu的最佳效能) 首先列举几个容易混淆的线程状态问题文末进行解答: ...
  • 什么是 RUNNABLE?与传统的ready状态的区别与传统的running状态的区别当I/O阻塞时如何看待RUNNABLE状态?Java虚拟机层面所暴露给我们的状态,与操作系统底层的线程...
  • 多线程-Java线程中的volatile int是安全的吗?Java线程中的易失性int是否是线程安全的? 也就是说,可以安全地读取和写入它而无需锁定吗?6个解决方案66 votes是的,您可以安全地对其进行读取和写入操作-但是您不能...
  • Java线程整理

    2021-03-01 06:23:38
    一、线程池过于频繁的创建/销毁线程浪费性能,线程并发数量过多,JVM调度是抢占式的,线程上线文切换抢占系统资源导致阻塞。1.线程池线程数:一般CPU密集型:CPU+1IO密集型:[(线程等待时间+线程CPU时间)/线程CPU...
  • java线程基础版

    2021-04-22 21:10:32
    尽管线程对象的常用方法可以通过API文档来了解,但是有很多方法仅仅从API说明是无法详细了解的。我们先来说一下线程对象的几个重要的方法:首先我们来说明start()方法。一个线程对象生成后,如果...简单说你要做什么就...
  • java线程实现/创建的几种方式

    多人点赞 2021-08-06 12:44:51
    java线程的创建与实现 进程与线程 进程可以简单理解成一个可执行程序例如.exe,在Windows中的任务管理器中可以查看每一个进程,进程是一次程序的执行,是程序在数据集合上运行的过程,是系统资源调度的一个单位。...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 188,157
精华内容 75,262
关键字:

java线程的本质是什么

java 订阅