一文看尽Java-Thread

简介: 主要分成两部说起:Thread源码解读和常见面试题解答

一、前言


    主要分成两部说起:Thread源码解读和常见面试题解答,废话不多说开始;


二、源码解读


    首先看下构造函数,构造函数都是通过调用init方法对属性进行初始化,主要是对线程组、线程名字、栈大小等信息进行初始化;init内部通过调用currentThread本地方法,获取当前的线程,这个本地方法封装在JVM中,有兴趣的可以看下这个这个链接查找下JVM实现https://hg.openjdk.java.net/jdk8u,接下来对ThreadGroup的判断,如果没有传入线程组的话, 第一是使用SecurityManager中的ThreadGroup, 如果从SecurityManager 中获取不到ThreadGroup(), 那么就从当前线程中获取线程组,最后做了检验和些参数的赋值,整体上相对比较简单;

   

private void init(ThreadGroup g, Runnable target, String name,
                    long stackSize) {
      init(g, target, name, stackSize, null);
  }
  private void init(ThreadGroup g, Runnable target, String name,
                    long stackSize, AccessControlContext acc) {
      if (name == null) {
          throw new NullPointerException("name cannot be null");
      }
      this.name = name.toCharArray();
      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();
      /*
       * 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();
  }
  public Thread() {
      init(null, null, "Thread-" + nextThreadNum(), 0);
  }
  public Thread(Runnable target) {
      init(null, target, "Thread-" + nextThreadNum(), 0);
  }
  Thread(Runnable target, AccessControlContext acc) {
      init(null, target, "Thread-" + nextThreadNum(), 0, acc);
  }
 // 线程名
  public Thread(String name) {
      init(null, null, name, 0);
  }
  //线程组和线程名
  public Thread(ThreadGroup group, String name) {
      init(group, null, name, 0);
  }
  //线程任务,线程名
  public Thread(Runnable target, String name){
      init(null, target, name, 0);
  }
  // 线程组, 线程任务, 线程名 ,栈大小
  public Thread(ThreadGroup group, Runnable target, String name, long stackSize) {
      init(group, target, name, stackSize);
  }

 接下来看下主要的属性:

   

// 类加载的时候,调用本地的注册本地方静态方法, 这个方法是本地方法
  private static native void registerNatives();
  static {
      registerNatives();
  }
  private volatile char  name[];
  private int            priority;
  private Thread         threadQ;
  private long           eetop;
  /* Whether or not to single_step this thread. */
  private boolean     single_step;
  /* Whether or not the thread is a daemon thread. */
  // 设设置这个线程是否是守护线程
  private boolean     daemon = false;
  /* JVM state */
  private boolean     stillborn = false;
  /* What will be run. */
  // 要执行的run方法的对象
  private Runnable target;
  /* The group of this thread */
  // 这个线程的线程组
  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 values pertaining to this thread. This map is maintained
   * by the ThreadLocal class. */
  ThreadLocal.ThreadLocalMap threadLocals = null;
  /*
   * InheritableThreadLocal values pertaining to this thread. This map is
   * maintained by the InheritableThreadLocal class.
   */
  ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
  /*
   * The requested stack size for this thread, or 0 if the creator did
   * not specify a stack size.  It is up to the VM to do whatever it
   * likes with this number; some VMs will ignore it.
   */
   // 给这个线程设置的栈的大小,默认为0 
  private long stackSize;
  /*
   * JVM-private state that persists after native thread termination.
   */
  private long nativeParkEventPointer;
  /*
   * Thread ID
   */
   //线程id
  private long tid;
  /* For generating thread ID */
  private static long threadSeqNumber;
  /* Java thread status for tools,
   * initialized to indicate thread 'not yet started'
   */
  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;
      }
  }
  /**
   * The minimum priority that a thread can have.
   */
   // 线程执行的最低优先级 为1
  public final static int MIN_PRIORITY = 1;
 /**
   * The default priority that is assigned to a thread.
   */
   // 线程默认的执行优先级为 5
  public final static int NORM_PRIORITY = 5;
  /**
   * The maximum priority that a thread can have.
   */
   // 线程执行的最高的优先级为 10
  public final static int MAX_PRIORITY = 10;

最后介绍下方法的作用和线程状态,源码都比较简单,没必进行过多的介绍,都是通过调

用JVM的本地方法实现;

  1005447-20190901200219214-975485936.png

   线程状态:

  1005447-20190901201438092-1096669849.png


、常见面试题


   1.线程与进程的区别?

      进程是资源分配最小的单位,线程是CPU调度最小的单位;

      线程属于进程,共享进程分配的资源;

      进程属于抢占式调度,资源不相互共享;

   2.start和run的区别?

     run是Thread的一个普通的方法;

     start方法会创建一个新的子线程并启动;

   3.sleep与wait的区别?

     sleep是Thread方法,wait是Object的方法;

     wait方法只能在synchroized方法或者块中使用;

     Thread.sleep只会让出CPU,不会改变锁的行为;

     Object.wait不仅会让出CPU,同时还会释放占有同步资源的锁;

   4.线程状态的转化?

    图中将WAITINGTIMED_WAITING 两个状态合并为WAITING ,没有分开,大家不要搞错;

   1005447-20190901202849577-807896020.png

 

 

    5.如何处理线程的返回值?

       主线程等待法,使用while等待主线程返回值;

       join阻塞当前线程以等待子线程;

       通过FuTureTask获取子线程的返回值;

public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        String value="test";
        System.out.println("start");
        Thread.sleep(5000);
        System.out.println("end");
        return value;
    }
}
public class FutureTaskDemo {
    public static void main(String[] main) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask=new FutureTask<String>(new MyCallable());
        new Thread(futureTask).start();
        if (!futureTask.isDone()){
            System.out.println("waiting");
        }
        System.out.println("return"+futureTask.get());
    }
}

       通过线程池获取返回值;

public class ThreadPoolDemo {
    public static void main(String[] args){
        ExecutorService executorService= Executors.newCachedThreadPool();
        Future<String> futureTask=executorService.submit(new MyCallable());
        if (!futureTask.isDone()){
            System.out.println("wait");
        }
        try {
            System.out.println(futureTask.get());
        }catch (InterruptedException ex){
            ex.printStackTrace();
        }catch (ExecutionException ex){
            ex.printStackTrace();
        }finally {
            executorService.shutdown();
        }
    }
}

   6.Thread和Runnable?

      Thread是类,Runnable是接口,Thread是Runnable实现;

      类的继承单一原则,Runnable是更高层次的抽象;

相关文章
|
3月前
|
Java 开发者
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
Java面试题:请解释内存泄漏的原因,并说明如何使用Thread类和ExecutorService实现多线程编程,请解释CountDownLatch和CyclicBarrier在并发编程中的用途和区别
41 0
|
2月前
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
49 7
|
1月前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
2月前
|
Java
在 Java 中 Runnable 与 Thread 的适时运用
【8月更文挑战第22天】
21 0
|
2月前
|
Java
Exception in thread "main" java.lang.UnsatisfiedLinkError: xxx()V
Exception in thread "main" java.lang.UnsatisfiedLinkError: xxx()V
15 0
|
4月前
|
Java
Java中,有两种主要的方式来创建和管理线程:`Thread`类和`Runnable`接口。
【6月更文挑战第24天】Java创建线程有两种方式:`Thread`类和`Runnable`接口。`Thread`直接继承受限于单继承,适合简单情况;`Runnable`实现接口可多继承,利于资源共享和任务复用。推荐使用`Runnable`以提高灵活性。启动线程需调用`start()`,`Thread`直接启动,`Runnable`需通过`Thread`实例启动。根据项目需求选择适当方式。
51 2
|
4月前
|
Java 开发者
JAVA多线程初学者必看:为何选择继承Thread还是Runnable,这其中有何玄机?
【6月更文挑战第19天】在Java中创建线程,可选择继承Thread类或实现Runnable接口。继承Thread直接运行,但限制了多重继承;实现Runnable更灵活,允许多线程共享资源且利于代码组织。推荐实现Runnable接口,以保持类的继承灵活性和更好的资源管理。
52 2
|
4月前
|
Java 开发者
告别单线程时代!Java 多线程入门:选继承 Thread 还是 Runnable?
【6月更文挑战第19天】在Java中,面对多任务需求时,开发者可以选择继承`Thread`或实现`Runnable`接口来创建线程。`Thread`继承直接但限制了单继承,而`Runnable`接口提供多实现的灵活性和资源共享。多线程能提升CPU利用率,适用于并发处理和提高响应速度,如在网络服务器中并发处理请求,增强程序性能。不论是选择哪种方式,都是迈向高效编程的重要一步。
37 2
|
4月前
Exception in thread "main" java.lang.IllegalArgumentException: U+6570 ('.notdef') is not available in the font Helvetica-Bold, encoding: WinAnsiEncoding 这个问题如何解决
【6月更文挑战第19天】Exception in thread "main" java.lang.IllegalArgumentException: U+6570 ('.notdef') is not available in the font Helvetica-Bold, encoding: WinAnsiEncoding 这个问题如何解决
421 2
|
4月前
|
关系型数据库 分布式数据库 数据库
PolarDB操作报错合集之遇到报错:Exception in thread "main" java.lang.NoClassDefFoundError: jpcap/JpcapCaptor,该怎么解决
PolarDB是阿里云推出的一种云原生数据库服务,专为云设计,提供兼容MySQL、PostgreSQL的高性能、低成本、弹性可扩展的数据库解决方案,可以有效地管理和优化PolarDB实例,确保数据库服务的稳定、高效运行。以下是使用PolarDB产品的一些建议和最佳实践合集。