这并不是一种新的实现线程的方式,只是另外的一种写法。比如有些情况我们的线程就想执行一次,以后就用不到了。那么像上面两种方式都还要再定义一个类,显得比较麻烦,我们就可以通过匿名内部类的方式来实现。使用内部类实现依然有两种,分别是继承Thread类和实现Runnable接口。代码如下:
package com.hy.thread.t3; public class DemoThread { public static void main(String[] args) { // 基于子类的实现 new Thread() { public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印当前线程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } }; }.start(); // 基于接口的实现 new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印当前线程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); // 主线程的方法 while (true) { System.out.println(Thread.currentThread().getName() + " is running ... "); // 打印当前线程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } }
可以想象一下,我能不能既基于接口,又基于子类呢?像下面的代码会执行出什么样子呢?
package com.hy.thread.t3; public class DemoThred2 { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println("runnable is running ... "); // 打印当前线程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } } }) { public void run() { while (true) { System.out.println("sub is running ... "); // 打印当前线程的名字 try { Thread.sleep(1000); // 休息1000ms } catch (InterruptedException e) { e.printStackTrace(); } } }; }.start(); } }
运行结果如下:
sub is running ... sub is running ... sub is running ...
我们可以看到,其实是基于子类的执行了,为什么呢,其实很简单,我们先来看一下为什么不基于子类的时候Runnable的run方法可以执行。这个要从Thread的源码看起,下面是我截取的代码片段。
public Thread(Runnable target) init(null, target, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null, true); } private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) { if (name == null) { throw new NullPointerException("name cannot be null"); } this.name = name; Thread parent = currentThread(); SecurityManager security = System.getSecurityManager(); if (g == null) { /* Determine if it's an applet or not */ /* If there is a security manager, ask the security manager what to do. */ if (security != null) { g = security.getThreadGroup(); } /* If the security doesn't have a strong opinion of the matter use the parent thread group. */ if (g == null) { g = parent.getThreadGroup(); } } /* checkAccess regardless of whether or not threadgroup is explicitly passed in. */ g.checkAccess(); /* * Do we have the required permissions? */ if (security != null) { if (isCCLOverridden(getClass())) { security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); } } g.addUnstarted(); this.group = g; this.daemon = parent.isDaemon(); this.priority = parent.getPriority(); if (security == null || isCCLOverridden(parent.getClass())) this.contextClassLoader = parent.getContextClassLoader(); else this.contextClassLoader = parent.contextClassLoader; this.inheritedAccessControlContext = acc != null ? acc : AccessController.getContext(); this.target = target; // 注意这里 setPriority(priority); if (inheritThreadLocals && parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); /* Stash the specified stack size in case the VM cares */ this.stackSize = stackSize; /* Set thread ID */ tid = nextThreadID(); }
其实上面的众多代码就是为了表现 this.target = target 那么target是什么呢,是Thread类的成员变量。那么在什么地方用到了target呢?下面是run方法的内容。
@Override public void run() { if (target != null) { target.run(); } }
我们可以看到,如果通过上面的构造方法传入target,那么就会执行target中的run方法。可能有朋友就会问了,我们同时继承Thread类和实现Runnable接口,target不为空,那么为何不执行target的run呢。不要忘记了,我们在子类中已经重写了Thread类的run方法,因此run方法已经不在是我们看到的这样了。那当然也就不回执行target的run方法。