一、Thread的start与run方法的区别
1.1 t.run()的时候
从运行结果可以知道,main里面当前的线程与attck里面的线程是一样的
public class ThreadTest {
private static void attack(){
System.out.println("Fight");
System.out.println("Current Thread is : "+Thread.currentThread().getName());
}
public static void main(String[] args){
//线程
Thread t = new Thread(){
public void run(){
attack();
}
};
//打印现在的主线程
System.out.println("Current main thread is : "+Thread.currentThread().getName());
t.run();
}
}
1.2 t.start()的时候
从运行结果可以知道,main函数的线程为main,而attack里面的线程为Thread-0
public class ThreadTest {
private static void attack(){
System.out.println("Fight");
System.out.println("Current Thread is : "+Thread.currentThread().getName());
}
public static void main(String[] args){
//线程
Thread t = new Thread(){
public void run(){
attack();
}
};
//打印现在的主线程
System.out.println("Current main thread is : "+Thread.currentThread().getName());
t.start();
}
}
1.3 比较与分析
通过1.1与1.2的比较,当我们使用run的时候,会延用主线程运行方法,而使用start的时候,则会使用一个非main的线程来运行方法。
Thread-0是通过start方法来创建的,查看start的源码,发现主要方法为private native void start0(),native会调用外部一些非java的源码,查看网站http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/1e8cdf311133/src/share/native/java/lang/Thread.c可找到start0的源码,发现是关于JVM的,如下图标注:
查看网站http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/760b28d87178/src/share/vm/prims/jvm.h找到关于jvm相关内容,查找关键字jvm_start,发现它是会创建一个新的线程,如下图标注:
期间传入了thread_entry,查找thread_entry,可以看到它会call虚拟机并传入run方法的名字,即它会创建一个新的线程去执行我们run方法里的内容,如下图所示:
通过比较,java线程中的start()会调用JVM_StartThread创建一个新的子线程,然后通过thread_entry中的run方法去调用run(),区别即:调用strat()方法会创建一个新的子线程并启动,run()方法只是Thread的一个普通方法的调用。
二、Thread和Runnable的关系
2.1 Thread是一个类,Runnable是一个接口
Thread实现Runnable接口,使得run支持多线程,而Runnable接口非常简单,只有一个抽象的run方法,即Runnable接口并没有多线程的特性,它是依赖Thread前面讲的start方法去创建子线程,再在子线程里面调用这个Thread的run方法,因类的单一继承原则,推荐多使用Runnable接口
2.2 Thread实现多线程
public class MyThread extends Thread{
private String name;
public MyThread(String name){
this.name = name;
}
@Override
public void run(){
for(int i = 0; i < 10; i++){
System.out.println("Thread start : " + this.name+" , i = "+i);
}
}
}
public class ThreadDemo {
public static void main(String[] args){
MyThread mt1 = new MyThread("Thread1");
MyThread mt2 = new MyThread("Thread2");
MyThread mt3 = new MyThread("Thread3");
mt1.start();
mt2.start();
mt3.start();
}
}
由运行结果可以看到,发现线程是交互执行,顺序不确定的,达到了多线程的目的
2.3 Runnable实现多线程
查看Thread可以发现到一个构造方法,即证明Runnable依赖Thread
public Thread(Runnable target) {
this(null, target, "Thread-" + nextThreadNum(), 0);
}
public class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name){
this.name=name;
}
@Override
public void run() {
for(int i = 0; i < 10; i++){
System.out.println("Thread start : " + this.name+" , i = "+i);
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable mr1 = new MyRunnable("Runnable1");
MyRunnable mr2 = new MyRunnable("Runnable2");
MyRunnable mr3 = new MyRunnable("Runnable3");
Thread t1 = new Thread(mr1);
Thread t2 = new Thread(mr2);
Thread t3 = new Thread(mr3);
t1.start();
t2.start();
t3.start();
}
}
由运行结果可以看到,发现线程是交互执行,顺序不确定的,达到了多线程的目的
三、处理线程的返回值
3.1 实现方式
根据线程相关的业务逻辑,需要放在run()方法里面执行,而run方法是没有参数,也没有返回值的,如下:
public abstract void run();
传参可以通过构造函数传参、成员变量传参、回调函数传参,但多数情况下,有些程序的执行是依赖子任务的执行去进行的,实现返回值便可以通过主线程等待法,即让主线程循环等待子线程结束、 Thread类的join()阻塞当前线程以等待子线程处理完毕,即阻塞主线程,等待join里面的子线程完成为止、 通过Callable接口实现,通过FutureTask或者线程池,即可以获取Future对象,在这个对象上调用get就可以获取Callable任务无返回的Object
3.2 主线程等待法例子
3.2.1 返回值为null
根据之前讲的run与start例子中知道,当主线程main运行到t.start()的时候的时候,运行CycleWait的子线程还没给value赋值的时候,主线程main便打印了value,所以值为null
public class CycleWait implements Runnable{
private String value;
@Override
public void run() {
try {
//让当前线程等待5秒后再执行
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = "We find a kinda wonderland now!";
}
public static void main(String[] args){
CycleWait cycleWait = new CycleWait();
Thread t = new Thread(cycleWait);
t.start();
System.out.println(cycleWait.value);
}
}
3.2.2 修改代码,让主线程等待子线程给value赋值完毕
主线程等待方法实现简单,但是需要自己实现等待逻辑,例如下面的while方法,在项目中如果等待的子线程较多,代码便会显得臃肿,循环多久还无法精确低控制
public class CycleWait implements Runnable{
private String value;
@Override
public void run() {
try {
//让当前线程等待5秒后再执行
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = "We find a kinda wonderland now!";
}
public static void main(String[] args) throws InterruptedException {
CycleWait cycleWait = new CycleWait();
Thread t = new Thread(cycleWait);
t.start();
//让主线程等待
while(cycleWait.value==null){
Thread.currentThread().sleep(100);
}
System.out.println(cycleWait.value);
}
}
3.3 join方法阻塞主线程
使用方便,比主线程等待发更加精准低控制,但join方法的力度不够细,例如以上面循环输出i为例子,当我们想Runnable1中i=5的时候启动Runnable2,join便无法实现
public class CycleWait implements Runnable{
private String value;
@Override
public void run() {
try {
//让当前线程等待5秒后再执行
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = "We find a kinda wonderland now!";
}
public static void main(String[] args) throws InterruptedException {
CycleWait cycleWait = new CycleWait();
Thread t = new Thread(cycleWait);
t.start();
//join阻塞
t.join();
System.out.println(cycleWait.value);
}
}
3.4 实现Callable接口
3.4.1 Callable接口
//Callable接口,call方法返回一个泛型
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
3.4.2 FutureTask类主要方法
FutureTask实现RunnableFuture接口,RunnableFuture接口实现Runnable接口,所以3.4.4的task传入Thread
//构造函数可以接受Callable实现类的实例
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
//用来判断传来的Callable实现类的call的方法是否已经执行完成
public boolean isDone() {
return state != NEW;
}
//用来阻塞当前调用它的线程,直到callable实现类的call方法实现完毕为止
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
//含有timeout的get方法,加入超时机制,如果在超时的时间内还没获取call的值,就抛出超时异常
public V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
if (unit == null)
throw new NullPointerException();
int s = state;
if (s <= COMPLETING &&
(s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
throw new TimeoutException();
return report(s);
}
3.4.3 MyCallable.java
public class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
String value ="test";
System.out.println("I am ready to work!");
//等待5秒
Thread.currentThread().sleep(5000);
System.out.println("task done!");
return value;
}
}
3.4.4 FutureTaskDemo,通过FutureTask获取返回值
public class FutureTaskDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//futuretask传入callable
FutureTask<String> task = new FutureTask<String>(new MyCallable());
new Thread(task).start();
if(!task.isDone()){
System.out.println("task has not finished,plz wait");
}
//task.get就会获取到MyCallable的return value
System.out.println("task return : "+task.get());
}
}
3.5 通过线程池获取返回值
public class ThreadPoolDemo {
public static void main(){
//创建线程池
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
//提交mycallable任务,返回的是future对象
Future<String> future = newCachedThreadPool.submit(new MyCallable());
if(!future.isDone()){
System.out.println("task has not finished,plz wait");
}
try {
System.out.println("task return : "+future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}finally {
newCachedThreadPool.shutdown();
}
}
}
3.6 future和线程池结果
“task done”等字符串会等待MyCallable5秒后return value后才显示,如下图所示:
四、线程的状态
-
new:新建,创建后尚未启动的线程的状态,即新创建一个线程对象,可是没有调用start方法
-
Runnable:运行、包含Running和Ready,有可能正在执行也有可能在等待CPU分配时间,例如调用了start方法后便处于Runnable状态,处于Running的状态的线程在可运行的线程池, 处于Ready的状态的线程在线程池中,等待被调度选中,分配CPU使用时间
-
Waiting:无限期等待,不会被分配CPU执行时间,需要显示被唤醒,需要被其他线程显示地唤醒
- 没有设置Timeout参数的Object.wait()方法
- 没有设置Timeout参数的Thread.join()方法
- LockSupport.park()方法
-
Timed Waiting:限期等待,在一定时间后会由自动唤醒,不需要被其他线程显示地唤醒
- Thread.sleep()方法
- 设置了Timeout参数的Object.wait()方法
- 设置了Timeout参数的Thread.join()方法
- LockSupport.parkNanos()方法
- LockSupport.parkUntil()方法
-
Blocked:阻塞,等待获取排它锁
-
Terminated:已终止线程的状态,线程已经结束执行,其时如果再调用则会报错
要显示被唤醒,需要被其他线程显示地唤醒
- 没有设置Timeout参数的Object.wait()方法
- 没有设置Timeout参数的Thread.join()方法
- LockSupport.park()方法
- Timed Waiting:限期等待,在一定时间后会由自动唤醒,不需要被其他线程显示地唤醒
- Thread.sleep()方法
- 设置了Timeout参数的Object.wait()方法
- 设置了Timeout参数的Thread.join()方法
- LockSupport.parkNanos()方法
- LockSupport.parkUntil()方法
- Blocked:阻塞,等待获取排它锁
- Terminated:已终止线程的状态,线程已经结束执行,其时如果再调用则会报错