zoukankan      html  css  js  c++  java
  • java线程基础知识----线程基础知识

      不知道从什么时候开始,学习知识变成了一个短期记忆的过程,总是容易忘记自己当初学懂的知识(fuck!),不知道是自己没有经常使用还是当初理解的不够深入.今天准备再对java的线程进行一下系统的学习,希望能够更好的理解使用java线程.

      1. 什么是线程,线程与进程的差别?(这一块内容我想我已经有了一个理解,这里就不再做记录了)

      2.java线程的状态:

    从百度上随便找了一张图,图中已经很清楚的标注了thread的各个状态以及状态的变化的场景.我们会在接下来的章节中进行相关讲解.

      3.java实现多线程的方式:

          A: 继承Thread类:(在下面的章节进行源码分析)

          

    public class ThreadTest {
        public static void main(String[] args) {
            Thread1 thread1 = new Thread1();
            thread1.start();
        }
    }
    class Thread1 extends Thread{
        public void run () {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

        B: 实现Runnable接口.

    public class ThreadTest {
        public static void main(String[] args) {
            Thread thread2 = new Thread(new Thread2());
            thread2.start();
        }
    }
    class Thread2 implements Runnable{
        @Override
        public void run() {
            try {
                Thread.sleep(100L);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

      4. 实现多线程的两种方法的差别:

        A. 众所周知java不允许多继承,那么我们集成Runnable接口实现多集成就能够很好的避免这个限制.

        B.集成Runnable接口实现多线程有利于程序操作共享资源(后面会提到)

        这个理解起来很简单:  我们继承了Thread类实现run方法之后我们可以发现这样一个问题,我们再进行线程实例化之后我们必须分别启动线程任务.

                  而我们实现Runnable接口的话,我们可以实例化多个Thread类来运行这个任务.

                  当然集成Thread类也并不是不能完成共享资源的分发,而是比较费劲.

      5.  实例化:我们在初始化Thread类的时候会调用Thread内部的init方法,即便是我们不提供任何参数.init函数的结构: private void init(ThreadGroup g, Runnable target, String name,long stackSize)

        参数有:ThreadGroup,Target,name.stackSize,其中ThreadGroup会递归去调用父类的getThreadGroup来进行初始化,等待初始化完成之后我们会通过ThreadGroup调用checkAccess()方法来检查当前线程是否有权限操作此线程.

    java源码:
     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();

      其中Thread类的daemon,priority属性会由父类继承.

      6.Thread类中的方法:

        Thread.sleep(): 此方法调用的是native的方法,本人不才,记得当初看过jdk源码,但是并没看懂底层实现。sleep方法是使当前线程休眠,讲cpu占用权交给其他任意优先级的线程。但是我们应该注意:sleep方法并不会释放对象锁。

        Thread.join():  记得当初查看api的时候觉得api对join方法的解释非常模糊。到底是谁等待谁结束,这有歧义。其实是这样的,在java7 api中介绍的很清楚,是调用join的线程等待被调用线程执行结束之后再开始执行。这里有一个很值得注意的问题,join的底层调用的是wait方法,而且是循环调用,源码如下:

     long base = System.currentTimeMillis();
            long now = 0;
    
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
    
            if (millis == 0) {
                while (isAlive()) {
                    wait(0);
                }
            } else {
                while (isAlive()) {
                    long delay = millis - now;
                    if (delay <= 0) {
                        break;
                    }
                    wait(delay);
                    now = System.currentTimeMillis() - base;
                }
            }

      我们可以看到源码中join方法会在while循环中一直调用wait方法,这是因为如果wait的时间是1000ms,如果在100ms的时候另外一个线程调用了notifyAll方法,那么线程就会苏醒。还要注意第二个问题,就是join调用wait方法,那么我们知道当main线程调用ThreadA.join的时候,main函数会获取ThreadA对象的锁。当ThreadA线程执行完成之后释放该对象锁。下面我们通过一个例子来验证一下上面的论述:我们新建三个线程,B,C,D,然后在B-C-D中进行循环调用。

    public class ThreadTest {
        public static ThreadB threadB = new ThreadB();
        public static void main(String[] args) throws InterruptedException{
            System.out.println("main线程开始调用B.join");
            threadB.start();
            threadB.join();
        }
    }
    class ThreadB  extends Thread{
        public void run(){
            try {
                System.out.println("ThreadB执行ThreadC.join");
                ThreadC threadC = new ThreadC();
                threadC.start();
                threadC.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    class ThreadC extends Thread{
        public void run(){
            try {
                System.out.println("ThreadC执行ThreadD.join");
                ThreadD threadD = new ThreadD();
                threadD.start();
                threadD.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    class ThreadD  extends Thread{
        public void run(){
            try {
                System.out.println("ThreadD执行ThreadB.join");
                ThreadTest.threadB.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    
    结果:
    main线程开始调用B.join
    ThreadB执行ThreadC.join
    ThreadC执行ThreadD.join
    ThreadD执行ThreadB.join

      可以发现我们的程序一直停留在这个位置,这是因为三个线程满足了死锁的条件,同时也可以证明,thread.join()的调用者必定会获取被调用者的锁。

      Thread.yield:  此方法与sleep方法类似,但是需要注意一个问题就是Thread.yield只能讲cpu的使用权转交给同等优先级的线程。

      Thread.start: 最后我们谈一谈Thread.start方法,想必大家都知道Thread.start方法会启动线程,并且执行run方法中的内容。你是否会想我们为什么不直接调用Thread.run来执行呢?其实是这样的,如果我们调用Thread.run来执行的话,jvm并不会真正的启动一个线程,而是将其当做一个普通的方法执行。而调用start的话,在start内部会调用start0方法来新建一个线程。

      至此: 线程的基础知识就结束了,下一章我们会学习关于线程锁的相关知识。

     

        

  • 相关阅读:
    两年来的读书小总结(20112013)
    给无边框窗体添加任务栏右键菜单
    使用 yum 命令安装本地安装QQ
    删除非空目录
    gcc安装
    WIN32::OLE操作之excel
    [题解] 组合数学13题
    [算法] 高斯消元及其应用
    [算法] Lucas 定理
    [算法] 最小费用最大流
  • 原文地址:https://www.cnblogs.com/liboBlog/p/6430551.html
Copyright © 2011-2022 走看看