java多线程学习1:线程基本api介绍

第一篇博文,说说为什么要写。
今天面了一个人,感觉他的知识不成体系,全靠零零碎碎的杂糅在一起。
所以,我要总结一下所学,构建知识网络。

1、生命周期
新建:new thread();
就绪:调用thread的start()方法,此为可运行状态。
running:当前线程抢到cpu执行权,正在运行。
blocked:堵塞状态,放弃当前cpu执行权。
dead:结束。

先分析一下:线程是哪些情况会进入blocked状态,哪些情况到dead状态。
1)blocked的几种情况:调用wait,被其他线程join,或者cpu调用导致放弃当前cpu执行权。 此时是 running->blocked,blocked结束后,线程回到runnnable状态。 runnable ->running -> blocked->runnable
2)dead:正常死亡或者程序直接挂掉。

2、创建并启动一个线程
先看看官方文档怎么说的。

There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started. 
The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread
第一个方式就是继承thread重写run方法,第二个方式,通过实现runnable接口,通过传参方式创建一个线程。   
1new Thread().start(); //继承方式
2new Thread(()->{}).start(); //runnable方式

分析一下两种方式:
1)我们创建一个线程后,实际执行单元都是run方法,那么为什么不能直接调用run方法启动一个线程。
看下源码:

public synchronized void 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) {
            }
        }
    }
start方法内部会执行一个native的start0方法,而start0方法会调用run方法。
start0就回去启动一个线程。
所以直接调用run,仅仅是执行一个方法并不会创建一个线程。

关于设计思路:
start方法,我们可以认为它是一个模版方法,同时thread又实现runnable。将具体执行单元(run)抽象出一个接口。 通过这种方式,既可以子类重写,也可以接口实现并注入。 接口方式,实际上一种由开发者自定义策略的方式。
2)new thread().start(); 都没有进行子类重写run。会启动一个线程吗
  会。只是没有执行单元而已。 下面是thread中run方法。
 public void run() {
        if (target != null) {
            target.run();
        }
    }

3、从构造函数分析线程创建的过程
看两个构造函数吧,昨天手臂懂了手术。将就一下自己的身体。

 public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }

 public Thread(ThreadGroup group, Runnable target) {
        init(group, target, "Thread-" + nextThreadNum(), 0);
    }
 private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
        init(g, target, name, stackSize, null, true);
    }

1)线程名问题:
在不传入线程名时,默认采用,”thread-“+0++方式。
2)在定义一个线程时,我们实际上需要定义
线程的treadgroup,执行单元,名字,栈深(有些jvm设了有用,有些设了没用)

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();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {

            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();
            }
        }

        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 (inheritThreadLocals && 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();
    }
1)如果没定义thread group,它回去找它爹的thread group。在main中,就是找main的threadgroup。main就是它爹。
2) stacksize不设就设0就用jvm默认的。threadgroup和 stacksize单独开一张

4、线程id、优先级、以及守护线程
thread提供了,线程id的get。priority、deamon的get、set的api。
id的设置在thread初始化完成。init方法中,tid = nextThreadID(); 0++;
而priority、deamon的默认值呢:

        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
    init方法中,和它爹的保持一致。当然我们通过set更改。
    其实在官方文档中也有说明。意思就是哪个线程创建了这个线程,他们的优先级和deamon就同

Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority. Each thread may or may not also be marked as a daemon. When code running in some thread creates a new Thread object, the new thread has its priority initially set equal to the priority of the creating thread, and is a daemon thread if and only if the creating thread is a daemon
详细说一下守护线程:
线程分为两种:
用户线程,守护线程。
举个例子解释下这两种的区别:
骑士护送公主去打怪升级,骑士负责守住打怪区域,公主负责打怪。公主挂了,骑士跟着挂。
上面例子可能不当,看官网。
Marks this thread as either a daemon thread or a user thread. The Java Virtual Machine exits when the only threads running are all daemon threads.
This method must be invoked before the thread is started.
什么意思呢:就是当jvm上面全是deamon的时候,jvm退出。
所以总结一下:
1、jvm中的所有用户进程死亡,deamon跟着死亡,jvm退出
2、setdeamon必须在thread.start之前。
3、然后补充一个,用过线程池的都知道。线程池会提供一个默认的threadfactory。
 那么线程池创建的线程时怎么样的呢。
 默认的会改变,线程的deamon(false)和优先级(normal)。 
 以后线程池的时候来说。

第一篇博客,同时技术不行。如果有看的,有疑惑,一起交流,3q。

你可能感兴趣的:(java多线程)