JDK源码学习(7)-Thread、Runnable与Callable

一、线程的基本概念

二、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方法,有返回值,有异常返回。

时间: 2024-10-03 22:09:12

JDK源码学习(7)-Thread、Runnable与Callable的相关文章

JDK源码学习系列07----Stack

                                                               JDK源码学习系列07----Stack 1.Stack源码非常简单 package java.util; public class Stack<E> extends Vector<E> { // 版本ID.这个用于版本升级控制,这里不须理会! private static final long serialVersionUID = 122446316454

JDK源码学习--String篇(二) 关于String采用final修饰的思考

JDK源码学习String篇中,有一处错误,String类用final[不能被改变的]修饰,而我却写成静态的,感谢CTO-淼淼的指正. 风一样的码农提出的String为何采用final的设计,阅读JDK源码的时候,有粗略的思考过,今天下班后又把<Thinking in Java>中关于final的内容重新看了一遍,对此写下一些关于自己的理解和想法. String类中final关键字的使用 final关键字,用来描述一块数据不能被改变,两种可能理由:设计.效率 final使用的三种情况:数据.方

JDK源码学习系列08----HashMap

                                                          JDK源码学习系列08----HashMap 1.HashMap简介 HashMap 是一个散列表,它存储的内容是键值对(key-value)映射. HashMap 继承于AbstractMap,实现了Map.Cloneable.java.io.Serializable接口. HashMap 的实现不是同步的,这意味着它不是线程安全的.它的key.value都可以为null.此外,

JDK源码学习LinkedList

LinkedList是List接口的子类,它底层数据结构是双向循环链表.LinkedList还实现了Deque接口(double-end-queue双端队列,线性collection,支持在两端插入和移除元素).所以LinkedList既可以被当作双向链表,还可以当做栈.队列或双端队列进行操作.文章目录如下: 1.LinkedList的存储实现(jdk 1.7.0_51) 2.LinkedList的读取实现 3.LinkedList的性能分析 下面我们进入正题,开始学习LinkedList. L

JDK源码学习系列06----Vector

                                            JDK源码学习系列06----Vector 1.Vector简介 Vector的内部是数组实现的,它和ArrayList非常相似,最大的不同就是 Vector 是线程安全(同步)的. public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.S

JDK源码学习----ArrayList

                                                                         JDK源码学习----ArrayList 1.ArrayList简介 ArrayList是基于Object[] 数组的,也就是我们常说的动态数组.它能很方便的实现数组的增加删除等操作. public class ArrayList<E> extends AbstractList<E> implements List<E>, R

JDK源码学习09----HashTable

                                                         JDK源码学习09----HashTable 1.HashTable简介 Hashtable 也是一个散列表,它存储的内容是键值对(key-value)映射. Hashtable 继承于Dictionary,实现了Map.Cloneable.java.io.Serializable接口. Hashtable 的函数都是同步的,这意味着它是线程安全的.它的key.value都不可以为n

JDK源码学习系列05----LinkedList

                                         JDK源码学习系列05----LinkedList 1.LinkedList简介 LinkedList是基于双向链表实现的,它也可以被当作堆栈.队列或双端队列进行操作. public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, jav

由JDK源码学习HashMap

HashMap基于hash表的Map接口实现,它实现了Map接口中的所有操作.HashMap允许存储null键和null值.这是它与Hashtable的区别之一(另外一个区别是Hashtable是线程安全的).另外,HashMap中的键值对是无序的.下面,我们从HashMap的源代码来分析HashMap的实现,以下使用的是Jdk1.7.0_51. 一.HashMap的存储实现 HashMap底层采用的是数组和链表这两种数据结构.当我们把key-value对put到HashMap时,系统会根据ha

由JDK源码学习ArrayList

ArrayList是实现了List接口的动态数组.与java中的数组相比,它的容量能动态增长.ArrayList的三大特点: ① 底层采用数组结构 ② 有序 ③ 非同步 下面我们从ArrayList的增加元素.获取元素.删除元素三个方面来学习ArrayList. ArrayList添加元素 因为ArrayList是采用数组实现的,其源代码比较简单.首先我们来看ArrayList的add(E e).以下代码版本是jdk7. public boolean add(E e) { // 检查数组容量 e