实现方式1(错误示例):
public class Main implements Callable<String>{ @Override public String call() throws Exception { // TODO Auto-generated method stub String str = "我是带返回值的子线程"; return str; } public static void main(String[] args) { Main main = new Main(); try { String str = main.call(); System.out.println(str); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
运行结果:
实现方式2(使用FutureTask):
public class Main implements Callable<String>{ @Override public String call() throws Exception { // TODO Auto-generated method stub String str = "我是带返回值的子线程"; return str; } public static void main(String[] args) { FutureTask<String> task = new FutureTask<String>(new Main()); new Thread(task).start(); try { if(!task.isDone()) { System.out.println("任务没有执行完成"); } System.out.println("等待中..."); Thread.sleep(3000); System.out.println(task.get()); } catch (InterruptedException | ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
运行结果:
实现方法3(使用线程池配合Future获取):
public class Main implements Callable<String>{ @Override public String call() throws Exception { // TODO Auto-generated method stub String str = "我是带返回值的子线程"; return str; } public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService newCacheThreadPool = Executors.newCachedThreadPool(); Future<String> future = newCacheThreadPool.submit(new Main()); if(!future.isDone()) { System.out.println("线程尚未执行结束"); } System.out.println("等待中"); Thread.sleep(300); System.out.println(future.get()); newCacheThreadPool.shutdown(); } }
运行结果:
线程的状态
Java线程主要分为以下六个状态:新建态(new),运行态(Runnable),无限期等待
(Waiting),限期等待(TimeWaiting),阻塞态(Blocked),结束(Terminated)。
新建(new)
新建态是线程处于已被创建但没有被启动的状态,在该状态下的线程只是被创建出来了,但并没有开始执行其内部逻辑。
运行(Runnable)
运行态分为Ready和Running,当线程调用start方法后,并不会立即执行,而是去争夺CPU,当线程没有开始执行时,其状态就是Ready,而当线程获取CPU时间片后,从Ready态转为Running态。
等待(Waiting)
处于等待状态的线程不会自动苏醒,而只有等待被其它线程唤醒,在等待状态中该线程不会被CPU分配时间,将一直被阻塞。以下操作会造成线程的等待:
- 没有设置timeout参数的Object.wait()方法。
- 没有设置timeout参数的Thread.join()方法。
- LockSupport.park()方法(实际上park方法并不是LockSupport提供的,而是在Unsafe中,LockSupport只是对其做了一层封装,可以看我的另一篇博客《锁》,里面对于ReentrantLock的源码解析有提到这个方法)。
限期等待(TimeWaiting)
处于限期等待的线程,CPU同样不会分配时间片,但存在于限期等待的线程无需被其它线程显式唤醒,而是在等待时间结束后,系统自动唤醒。以下操作会造成线程限时等待:
- Thread.sleep()方法。
- 设置了timeout参数的Object.wait()方法。
- 设置了timeout参数的Thread.join()方法。
- LockSupport.parkNanos()方法。
- LockSupport.parkUntil()方法。
阻塞(Blocked)
当多个线程进入同一块共享区域时,例如Synchronized块、ReentrantLock控制的区域等,会去整夺锁,成功获取锁的线程继续往下执行,而没有获取锁的线程将进入阻塞状态,等待获取锁。
结束(Terminated)
已终止线程的线程状态,线程已结束执行。
Sleep和Wait的区别
Sleep和Wait者两个方法都可以使线程进入限期等待的状态,那么这两个方法有什么区别呢?
- sleep方法由Thread提供,而wait方法由Object提供。
- sleep方法可以在任何地方使用,而wait方法只能在synchronized块或synchronized方法中使用(因为必须获wait方法会释放锁,只有获取锁了才能释放锁)。
- sleep方法只会让出CPU,不会释放锁,而wait方法不仅会让出CPU,还会释放锁。
测试代码:
public class Main{ public static void main(String[] args) { Thread threadA = new Thread(new ThreadA()); Thread threadB = new Thread(new ThreadB()); threadA.setName("threadA"); threadB.setName("threadB"); threadA.start(); threadB.start(); } public static synchronized void print() { System.out.println("当前线程:"+Thread.currentThread().getName()+"执行Sleep"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("当前线程:"+Thread.currentThread().getName()+"执行Wait"); try { Main.class.wait(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("当前线程:"+Thread.currentThread().getName()+"执行完毕"); } } class ThreadA implements Runnable{ @Override public void run() { // TODO Auto-generated method stub Main.print(); } } class ThreadB implements Runnable{ @Override public void run() { // TODO Auto-generated method stub Main.print(); } }
执行结果:
从上面的结果可以分析出:当线程A执行sleep后,等待一秒被唤醒后继续持有锁,执行之后的代码,而执行wait之后,立即释放了锁,不仅让出了CPU还让出了锁,而后线程B立即持有锁开始执行,和线程A执行了同样的步骤,当线程B执行wait方法之后,释放锁,然后线程A拿到锁打印了第一个执行完毕,然后线程B打印执行完毕。