线程通常都有五种状态,创建、就绪、运行、阻塞和死亡
实例化线程对象有所不同: extends Thread :t.start(); implements Runnable :new Thread(t).start();
实现Runnable接口比继承Thread类所具有的优势: 1):适合多个相同的程序代码的线程去处理同一个资源 2):可以避免java中的单继承的限制(不能访问父类的私有成员?) 3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立 4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
run方法:是线程的任务处理逻辑的入口方法,它由Java虚拟机在运行相应线程时直接调用,而不是由应用代码进行调用。 调用run方法其实就是当作普通的方法的方式调用。并没有创建一个线程,程序中依旧只有一个主线程,必须等到run()方法里面的代码执行完毕,才会继续执行下面的代码,这样就没有达到加入线程的目的。
start方法:是启动相应的线程。启动一个线程实际是请求Java虚拟机运行相应的线程,而这个线程何时能够运行是由线程调度器决定的。start()调用并不表示相应线程已经开始运行,这个线程可能稍后运行。 创建并启动一个线程,真正实现了多线程。无需等待run()方法中的代码执行完毕,就可以接着执行下面的代码。此时start()的这个线程处于就绪状态,当得到CPU的时间片后就会执行其中的run()方法。这个run()方法包含了要执行的这个线程的内容,run()方法运行结束线程终止。
start()方法能够异步的调用run()方法,但是直接调用run()方法却是同步的,无法达到多线程的目的。因此,只用通过调用线程类的start()方法才能达到多线程的目的。
volatile修饰singleton很有必要,voliatile防止了指令重排
在这里插入代码片在现在的java模型下,线程可以把变量保存在本地内存(比如寄存器)中,而不是直接在主内存中进行读写,这就可能造成一个线程在主内存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量的拷贝,造成数据不一致。
1.创建 Callable 接口的实现类,并实现 call() 方法,该 call() 方法将作为线程执行体,并且有返回值。 2.创建 Callable 实现类的实例,使用 FutureTask 类来包装 Callable 对象,该 FutureTask 对象封装了该 Callable 对象的 call() 方法的返回值。 3.使用 FutureTask 对象作为 Thread 对象的 target 创建并启动新线程。 4.调用 FutureTask 对象的 get() 方法来获得子线程执行结束后的返回值。
https://www.jianshu.com/p/27b7340d8470