Thread

线程的创建

java提供了三种创建线程的方法:

  • 通过继承 Thread 类本身;
  • 通过实现 Runnable 接口;
  • 通过 Callable 和 Future 创建线程。

继承Thread类

步骤:

  1. 继承Thread类
  2. 重写run方法
  3. 实例化该类,调用start方法

演示:

public class TestThread1{
    public static void main(String args[]){
        //3.实例化该类,调用start方法
        Thread1 t1 = new Thread1();
        t1.start();
        for(int i=0;i<=10;i++){
            System.out.println("cpu:MainThread");
        }
    }
}
//1.继承Thread类
class Thread1 extends Thread{
    //2.重写run()方法
    public void run(){
        for(int i=0;i<=10;i++){
            System.out.println("cpu:Thread1");
        }
    }
}

结果:

//输出结果不唯一,通过输出情况,说明确实新建了Thread1线程,因为出现了MainThread和Thread1抢占cpu的现象
cpu:Thread1
cpu:Thread1
cpu:Thread1
cpu:Thread1
cpu:Thread1
cpu:Thread1
cpu:Thread1
cpu:Thread1
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:Thread1
cpu:Thread1
cpu:Thread1

实现Runnable接口

步骤:

  1. 实现Runnable接口
  2. 重写run方法
  3. 将该类的实例作为参数实例化Thread类,调用调用start方法

演示:

public class TestThread2{
    public static void main(String args[]){
        Runnable1 r1 = new Runnable1();
        //3. 将该类的实例作为参数实例化Thread类,调用调用start方法
        Thread t = new Thread(r1);
        t.start();
        for(int i=0;i<10;i++){
            System.out.println("cpu:MainThread");
        }
    }
}
//1. 实现Runnable接口
class Runnable1 implements Runnable{
    //2. 重写run方法
    public void run(){
        for(int i=0;i<10;i++){
            System.out.println("cpu:Thread2");
        }
    }
}

结果:

cpu:Thread2
cpu:Thread2
cpu:Thread2
cpu:Thread2
cpu:Thread2
cpu:Thread2
cpu:Thread2
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:Thread2
cpu:Thread2
cpu:Thread2

Callable和Future

步骤:

  1. 实现Callable接口
  2. 重写call方法
  3. 将该类的实例作为参数实例化FutureTask类
  4. 将FutureTask对象作为参数实例化Thread,调用start方法

演示:

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
//1. 实现Callable接口
class Callable1 implements Callable<String> {
    //2. 重写call方法
    public String call() throws Exception {
        for (int i = 0; i < 10; i++) {
            System.out.println("cpu:Thread3");
        }
        return "hello world";
    }
}

public class TestThread3 { 
    public static void main(String[] args) {
        Callable1 c1 = new Callable1();
        //3. 将该类的实例作为参数实例化FutureTask类
        FutureTask<String> ft = new FutureTask<String>(c1);
        //4. 将FutureTask对象作为参数实例化Thread,调用start方法
        new Thread(ft, "Callable1").start();
        for (int i = 0; i < 10; i++) {
            System.out.println("cpu:MainThread");
        }

        try {
            System.out.println("子线程返回值:" + ft.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果:

cpu:Thread3
cpu:Thread3
cpu:Thread3
cpu:Thread3
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:Thread3
cpu:Thread3
cpu:Thread3
cpu:Thread3
cpu:Thread3
cpu:Thread3
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
cpu:MainThread
子线程返回值:hello world

总结

 三种创建线程的方法,都需要自定义线程的执行内容,并且线程启动执行的时候也是执行我们自定义的这段代码,为了实现这种功能,第一种创建线程的方法应用了java多态,后面两种应用了静态代理。
 对于实现Runnable或者Callable接口和继承Thread类这三种开辟新线程的方法的选择应该优先选择实现Runnable或者Callable接口这种方式去开辟一个新的线程。因为接口的实现可以实现多个,而类的继承只能是单继承。因此在开辟新线程时能够使用Runnable或者Callable接口就尽量不要使用从Thread类继承的方式来开辟新的线程,而Runnable和Callable的区别在于后者有返回值。

Thread属性

   private static native void registerNatives();
    static {
        registerNatives();
    }

    private volatile String name;  //线程的名称
    private int            priority; //线程的优先级
    private Thread         threadQ;  
    private long           eetop;  

    /* Whether or not to single_step this thread. */
    private boolean     single_step;

    //该线程是否是个守护线程
    private boolean     daemon = false;

    /* JVM state */
    private boolean     stillborn = false;

    /* What will be run. */
    private Runnable target;

    //该线程所在的线程组
    private ThreadGroup group;

    /* The context ClassLoader for this thread */
    private ClassLoader contextClassLoader;

    /* The inherited AccessControlContext of this thread */
    private AccessControlContext inheritedAccessControlContext;

    /* For autonumbering anonymous threads. */
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
        return threadInitNumber++;
    }

    //ThreadLocal类中对应该线程的一个map
    ThreadLocal.ThreadLocalMap threadLocals = null;

    /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
    ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

    //线程栈的深度,默认是0
    private long stackSize;

    /*
     * JVM-private state that persists after native thread termination.
     */
    private long nativeParkEventPointer;

    //线程id
    private long tid;

    //静态全局变量,用来产生线程id
    private static long threadSeqNumber;

 
    //线程的状态
    private volatile int threadStatus = 0;


    private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }

    /**
     * The argument supplied to the current call to
     * java.util.concurrent.locks.LockSupport.park.
     * Set by (private) java.util.concurrent.locks.LockSupport.setBlocker
     * Accessed using java.util.concurrent.locks.LockSupport.getBlocker
     */
    volatile Object parkBlocker;

    /* The object in which this thread is blocked in an interruptible I/O
     * operation, if any.  The blocker's interrupt method should be invoked
     * after setting this thread's interrupt status.
     */
    private volatile Interruptible blocker;
    private final Object blockerLock = new Object();

    /* Set the blocker field; invoked via sun.misc.SharedSecrets from java.nio code
     */
    void blockedOn(Interruptible b) {
        synchronized (blockerLock) {
            blocker = b;
        }
    }

    //下面三个是不同优先级指定
    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;

Thread类常用方法

init()

    //new Thread()本质是调用该方法
    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();//parent是创建该线程的线程引用
        //??
        SecurityManager security = System.getSecurityManager();
        if (g == null) {//没有指定线程组
            //??
            if (security != null) {
                g = security.getThreadGroup();
            }
            //继承其parent的线程组
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }
        //??
        g.checkAccess();

        //??
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }
        //线程组中未启动线程count+1
        g.addUnstarted();
        this.group = g; //初始化线程组
        this.daemon = parent.isDaemon(); //继承parent的daemon属性
        this.priority = parent.getPriority(); //继承parent的优先级
        //??
        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;//初始化Runnable target
        setPriority(priority);//设置线程优先级
        //??
        if (inheritThreadLocals && parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        this.stackSize = stackSize; //初始化栈的深度
        tid = nextThreadID(); //初始化线程Id
    }

//currentThread是一个本地方法
        public static native Thread currentThread();
//nextThreadID()方法,是线程安全的
        private static synchronized long nextThreadID() {
        return ++threadSeqNumber;
    }

start()

//vm在创建main线程和系统线程的时候不会调用start方法
public synchronized void start() {
        //threadStatus!=0说明该线程已经start过了,不允许两次start,抛出异常
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        //将该线程添加到线程组中表示该线程将要被启动
        group.add(this);
        boolean started = false;
        try {
            //核心方法
            start0();
            started = true;
        } finally {
            try {
                //如果线程启动失败,线程组中删除该线程
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
            }
        }
    }
//start0是本地方法
    private native void start0();

run()

    //run函数是线程执行的主体
    public void run() {
        //Runnable target
        if (target != null) {
            target.run();
        }
    }

通过前面分析的创建线程的不同方法,run函数的执行过程分为两种:

  1. 继承Thread,重写run函数,利用java多态,线程执行Thread子类的run函数
  2. 实现Runnable接口,重写run函数,初始化Thread对象的target属性,执行Thread类里的run函数

sleep(long)

    //当前的线程休眠millis时长,在此期间依然拥有锁(monitor)
    public static native void sleep(long millis) throws InterruptedException;

    举例:

public class Main extends Thread {
    static int number = 10;

    public void firstMethod() throws Exception {
        synchronized (this) {
            number += 100;
            System.out.println(number);
        }
    }

    public synchronized void secondMethod() throws Exception {
        Thread.sleep(2000);//主线程休眠两秒,但仍然拥有m对象的锁
        number *= 200;
    }

    public void run() {
        try {
            firstMethod();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception {
        Main m = new Main();
        m.secondMethod();
        m.start();
    }
}

结果:
两秒之后输出:2100

yield()

    //当前线程放弃cpu使用权,进入就绪状态,重新与优先级的线程共同竞争cpu
    public static native void yield();

    举例:

public class Main extends Thread {

    @Override
    public void run() {
        long beginTime = System.currentTimeMillis();
        int count = 0;
        for (int i = 0; i < 50000; i++) {
            Thread.yield();         // 注释该语句,执行变得很快
            ++count;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("用时:" + (endTime - beginTime) + "毫秒!");
    }

    public static void main(String[] args) {
        Main thread = new Main();
        thread.start();
    }
}
//结果时间差间接表明:线程执行期间放弃cpu再重新获取cpu的过程
结果:
没有注释:用时:41毫秒!
添加注释:用时:1毫秒!

join(long)

    //synchronized关键字:执行到join方法的线程要拥有内置锁,这也是该方法中可以调用wait的原因
    public final synchronized void join(long millis)
    throws InterruptedException {
        //记住当前时间
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        //即join()的执行情况
        if (millis == 0) {
            //isAlive()是判断调用join方法的对象线程是否存活,例如:m.join(),是判断m是否存活
            while (isAlive()) {
                //进入内置锁的wait队列
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                //等待millis时长之后退出
                if (delay <= 0) {
                    break;
                }
                //等待delay时长
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

    举例:

public class Thread1 extends Thread {
    public static void main(String[] args)  {
        System.out.println("进入线程" + Thread.currentThread().getName());
        Thread1 t1 = new Thread1();
        t1.start();
        try {
            System.out.println("线程" + Thread.currentThread().getName() + "等待");
            t1.join(1000);//Main线程进入t1内置锁的wait队列等待1000ms,在此期间执行t1线程
            System.out.println("线程" + Thread.currentThread().getName() + "执行完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void run() {
        System.out.println("进入线程" + Thread.currentThread().getName());
        try {
            Thread.currentThread().sleep(2000);//t1线程休眠2000ms
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程" + Thread.currentThread().getName() + "执行完毕");
    }
}

结果(稳定):
进入线程main
线程main等待
进入线程Thread-0
线程main执行完毕       
线程Thread-0执行完毕

总结:线程A执行过程中调用线程B的join(long mills)方法,线程A获得线程B的内置锁之后进入线程B的wait队列中等待mills时长(期间释放锁),线程B执行,如果在线程A等待期间,线程B执行完毕会notify线程A(在代码中没有体现,但是在底层结束线程的时候会有这个操作),因此线程的等待时间有可能小于mills
如果理解了join方法,下面一道面试题就迎刃而解了:

    //题目:三个线程t1,t2,t3,如何保证执行顺序为ti,t2,t3
       public class Thread1 {
            public static void main(String[] args) {
                method01();
            }

            private static void method01() {
               final Thread t1 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("t1 is finished");
                    }
                });
                final Thread t2 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            t1.join();//如果t2先于t1执行,t2进入t1内置锁的wait队列,等待t1执行完毕
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("t2 is finished");
                    }
                });
                Thread t3 = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            t2.join();//如果t3先于t2执行,t3进入t2内置锁的wait队列,等待t2执行完毕
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("t3 is finished");
                    }
                });

                t1.start();
                t2.start();
                t3.start();
            }
        }

参考资料:
http://www.cnblogs.com/xdp-gacl/p/3633936.html
https://www.jianshu.com/p/81a56497e073
http://blog.csdn.net/justloveyou_/article/details/54347954
http://blog.csdn.net/ll666634/article/details/78615505
https://wangchangchung.github.io/2016/12/05/Java常用类源码——Thread源码解析/

posted @ 2018-02-03 14:21  unbelievableme  阅读(947)  评论(0编辑  收藏  举报