线程的创建

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

jdk源码->多线程->Thread的更多相关文章

  1. 一点一点看JDK源码(五)java.util.ArrayList 后篇之Spliterator多线程遍历

    一点一点看JDK源码(五)java.util.ArrayList 后篇之Spliterator多线程遍历 liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看J ...

  2. JDK源码那些事儿之浅析Thread上篇

    JAVA中多线程的操作对于初学者而言是比较难理解的,其实联想到底层操作系统时我们可能会稍微明白些,对于程序而言最终都是硬件上运行二进制指令,然而,这些又太过底层,今天来看一下JAVA中的线程,浅析JD ...

  3. 曹工说JDK源码(2)--ConcurrentHashMap的多线程扩容,说白了,就是分段取任务

    前言 先预先说明,我这边jdk的代码版本为1.8.0_11,同时,因为我直接在本地jdk源码上进行了部分修改.调试,所以,导致大家看到的我这边贴的代码,和大家的不太一样. 不过,我对源码进行修改.重构 ...

  4. JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue

    JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...

  5. 【并发编程】【JDK源码】CAS与synchronized

    线程安全 众所周知,Java是多线程的.但是,Java对多线程的支持其实是一把双刃剑.一旦涉及到多个线程操作共享资源的情况时,处理不好就可能产生线程安全问题.线程安全性可能是非常复杂的,在没有充足的同 ...

  6. 【并发编程】【JDK源码】J.U.C--AQS (AbstractQueuedSynchronizer)(1/2)

    J.U.C实现基础 AQS.非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),concurrent包中的基础类都是使用这种模式来实现的.而concurren ...

  7. 【并发编程】【JDK源码】J.U.C--AQS 及其同步组件(2/2)

    原文:慕课网高并发实战(七)- J.U.C之AQS 在[并发编程][JDK源码]AQS (AbstractQueuedSynchronizer)(1/2)中简要介绍了AQS的概念和基本原理,下面继续对 ...

  8. JDK源码分析之concurrent包(二) -- 线程池ThreadPoolExecutor

    上一篇我们简单描述了Executor框架的结构,本篇正式开始并发包中部分源码的解读. 我们知道,目前主流的商用虚拟机在线程的实现上可能会有所差别.但不管如何实现,在开启和关闭线程时一定会耗费很多CPU ...

  9. 关于JDK源码:我想聊聊如何更高效地阅读

    简介 大家好,我是彤哥,今天我想和大家再聊聊JDK源码的几个问题: 为什么要看JDK源码 JDK源码的阅读顺序 JDK源码的阅读方法 为什么要看JDK源码 一,JDK源码是其它所有源码的基础,看懂了J ...

随机推荐

  1. 三目运算:and/or 技巧

    三目运算:and/or 技巧 and, or 联合起来有个小技巧: print 2 < 3 and True or False 说明:     如果  2 小于 3 了,则输出 True ,  ...

  2. 一次完整的http的请求过程

    一个完整的http的完成请求过程: 输入网址-> 域名解析-> tcp的三次握手-> 建立tcp连接后发起http 请求-> 服务器响应http ,发送数据给浏览器->  ...

  3. vb代码之------画一个半透明矩形

    入吾QQ群183435019 (学习 交流+唠嗑). 废话不说,咱们来看代码吧 程序结果运行如下 需要如下API 1:GdipCreateFromHDC 功能:创建设备场景相对应的绘图区域(相当于给设 ...

  4. java字符串比较

    我最近刚学java,今天编程的时候就遇到一个棘手的问题,就是关于判断两个字符串是否相等的问题.在编程中,通常比较两个字符串是否相同的表达式是"==",但在java中不能这么写.在j ...

  5. motan负载均衡/zookeeper集群/zookeeper负载均衡的关系

    motan/dubbo支持负载均衡.zookeeper有集群的概念.zookeeper似乎也能做负载均衡,这3者是什么关系呢? 3个概念:motan/dubbo负载均衡.zookeeper集群.zoo ...

  6. Java Web应用集成OSGI

    对OSGI的简单理解 就像Java Web应用程序需要运行在Tomcat.Weblogic这样的容器中一样.程序员开发的OSGI程序包也需要运行在OSGI容器中.目前主流的OSGI容器包括:Apach ...

  7. 51Nod 1080 两个数的平方和(数论,经典题)

    1080 两个数的平方和 基准时间限制:1 秒 空间限制:131072 KB 分值: 5         难度:1级算法题 给出一个整数N,将N表示为2个整数i j的平方和(i <= j),如果 ...

  8. Gym 100952D&&2015 HIAST Collegiate Programming Contest D. Time to go back【杨辉三角预处理,组合数,dp】

    D. Time to go back time limit per test:1 second memory limit per test:256 megabytes input:standard i ...

  9. BZOJ 1968: [Ahoi2005]COMMON 约数研究(新生必做的水题)

    1968: [Ahoi2005]COMMON 约数研究 Time Limit: 1 Sec  Memory Limit: 64 MB Submit: 2351  Solved: 1797 [Submi ...

  10. [bzoj 2017] [Usaco2009 Nov]硬币游戏

    一个多月没更博客了..(期间明白了自己有多傻逼. 这种问题大概就倒着做... f[i][j]:表示考虑剩下的硬币i..n,且之前的人取了j个时,先手最多拿到的钱数.aft[i]:表示硬币i..n的总钱 ...