一、线程的基本概念
二、java.lang.Thread
1. 构造函数:
public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) { //name必须不能为空。 if (name == null) { throw new NullPointerException("name cannot be null"); } // private volatile char name[];为name的声明 this.name = name.toCharArray(); //父线程为启动线程 Thread parent = currentThread(); //打开java的安全管理器 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 (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(); }
2. run和start
@Override public void run() { // private Runnable target;该target可以为runnable的run。适用于实现runnable接口的初始化线程的方法。 if (target != null) { target.run(); } }
public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ if (threadStatus != 0) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group‘s list of threads * and the group‘s unstarted count can be decremented. */ group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } private native void start0();
3.join()主要是等待该线程死亡。主要作用是该线程执行完成后才会运行join()后面的方法。
public final void join() throws InterruptedException { join(0); } public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; //如果时间小于0,则返回异常 if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { //如果时间为0,则一直等待直到当前线程死亡,即isAlive为false;每次循环等待的时间为0 while (isAlive()) { wait(0); } } else { //如果时间大于0,需要判断当前时间与join方法调用时间之间的差距,如果时间差大于参数设置的时间,则不再等待循环结束直接跳出 while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
例子:1.不调用join的情况
public class TestThread { //main() public static void main(String[] args) throws InterruptedException { MyThread thread = new MyThread(); MyThread thread1 = new MyThread(); thread.start(); thread1.start(); } } class MyThread extends Thread { public void run() { int i = 0; while (true) { System.out.println(this.toString() + ":" + (i++)); try { sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
输出结果为:
Thread[Thread-1,5,main]:0 Thread[Thread-0,5,main]:0 Thread[Thread-0,5,main]:1 Thread[Thread-1,5,main]:1 Thread[Thread-1,5,main]:2 Thread[Thread-0,5,main]:2 Thread[Thread-1,5,main]:3 Thread[Thread-0,5,main]:3
可以看到并没有相应的顺序和规律。
2.调用join()无参数的情况
修改main()方法为:
public static void main(String[] args) throws InterruptedException { MyThread thread = new MyThread(); MyThread thread1 = new MyThread(); thread.start(); thread.join(); thread1.start(); thread1.join(); }
输出结果为:
Thread[Thread-0,5,main]:0 Thread[Thread-0,5,main]:1 Thread[Thread-0,5,main]:2 Thread[Thread-0,5,main]:3 Thread[Thread-0,5,main]:4 Thread[Thread-0,5,main]:5 Thread[Thread-0,5,main]:6 Thread[Thread-0,5,main]:7
可以看到一直运行Thread-0这个线程,不会运行另一个线程,这是由于会一直等待Thread-0线程死亡,但由于是死循环,因此不会结束。
3.调用join()有参数的情况
修改mian()方法为:
public static void main(String[] args) throws InterruptedException { MyThread thread = new MyThread(); MyThread thread1 = new MyThread(); thread.start(); Long tt1=System.currentTimeMillis(); thread.join(100); Long ee1=System.currentTimeMillis(); System.out.println(ee1-tt1); thread1.start(); Long tt2=System.currentTimeMillis(); thread1.join(100); Long ee2=System.currentTimeMillis(); System.out.println(ee2-tt2); }
输出结果为:
Thread[Thread-0,5,main]:0 100 Thread[Thread-1,5,main]:0 100 Thread[Thread-0,5,main]:1 Thread[Thread-1,5,main]:1 Thread[Thread-0,5,main]:2 Thread[Thread-1,5,main]:2
可以看到在Thread-0开始后,调用join(100)后,会等待100毫秒后,运行Thread-1的run方法。而不是无限等待Thread-1的死亡。
该方法的主要内容可以保证主线程不会优先于子线程先死亡,即如果主线程启动一个子线程,可以在子线程中调用主线程的join方法,这样就可以保证子线程死亡后主线程才会继续往下运行。
三、java.lang.runnable
runnable为接口,只有一个抽象方法run。必须实现该方法才能实现线程的调用。代码如下:
public interface Runnable { public abstract void run(); }
四、java.util.concurrent.Callable与java.util.concurrent.FutureTask
1.该类为第三种初始化线程的方法。即实现Callable中call方法,并用FutureTask包装Callable类,通过Thread包装FutureTask之后调用start方法开启线程。call方法有返回值。调用方式如下:
public class TestCallable implements Callable<String> { public static void main(String[] args) { TestCallable testCallable=new TestCallable(); FutureTask futureTask=new FutureTask<String>(testCallable); for(int i=0;i<100;i++){ System.out.println(Thread.currentThread().getName()+"循环变量i的值"+i); if(i==70){ new Thread(futureTask,"有返回的线程").start(); } if(i==80){ try { System.out.println("子线程返回值"+futureTask.get()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } @Override public String call() throws Exception { int i = 0; for (; i < 100; i++) { System.out.println(Thread.currentThread().getName() + ":" + i); } return String.valueOf(i); } }
2.java.util.concurrent.Callable的代码为:可见逻辑主要是通过实现call的方法,并且有返回值
public interface Callable<V> { V call() throws Exception; }
3.java.util.concurrent.FutureTask
构造函数主要是包装Callable类。该类实现接口interface RunnableFuture<V> extends Runnable, Future<V>,因此可以使用Thread进行包装并调用start启动线程
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }
实现接口的run方法。
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { //result为callable的call()方法的返回值 V result; boolean ran; try { //调用call并且将ran设置为true result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } //将结果传给futureTask的对象 if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
五、Thread、runnable与callable的区别。
1.runnable与callable初始化后必须作为Thread的参数进行封装,并调用Thread的start方法进行启动线程。如下:
MyThread thread = new MyThread(); thread.start(); MyRunable runable=new MyRunable(); Thread runnableThread=new Thread(runable); runnableThread.start();
2.runnable与callable只有一个方法,如果需要调用sleep等方法,需要调用Thread中的方法。
3.建议使用runnable与callable实现线程的class,主要是因为runnable为接口,java中不支持多继承,但支持多接口,如果继承Thread来实现线程,那么就无法再继承别的父类了。
4.runnable实现run方法;callable实现call方法,有返回值,有异常返回。