• 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源码解析/

  • 相关阅读:
    chrome书签插件
    Js箭头函数和lambda
    CSS水平或垂直居中技巧
    前端需要注意的SEO优化
    OpenCV图像识别初探-50行代码教机器玩2D游戏
    机器学习笔记(十一)----降维
    基于Docker搭建分布式消息队列Kafka
    一个经典面试题:如何保证缓存与数据库的双写一致性?
    Flask 蓝图机制及应用
    软件开发团队如何管理琐碎、突发性任务
  • 原文地址:https://www.cnblogs.com/kundeg/p/8409199.html
Copyright © 2020-2023  润新知