jdk源码->多线程->Thread
线程的创建
java提供了三种创建线程的方法:
- 通过继承 Thread 类本身;
- 通过实现 Runnable 接口;
- 通过 Callable 和 Future 创建线程。
继承Thread类
步骤:
- 继承Thread类
- 重写run方法
- 实例化该类,调用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接口
步骤:
- 实现Runnable接口
- 重写run方法
- 将该类的实例作为参数实例化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
步骤:
- 实现Callable接口
- 重写call方法
- 将该类的实例作为参数实例化FutureTask类
- 将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函数的执行过程分为两种:
- 继承Thread,重写run函数,利用java多态,线程执行Thread子类的run函数
- 实现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的更多相关文章
- 一点一点看JDK源码(五)java.util.ArrayList 后篇之Spliterator多线程遍历
一点一点看JDK源码(五)java.util.ArrayList 后篇之Spliterator多线程遍历 liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看J ...
- JDK源码那些事儿之浅析Thread上篇
JAVA中多线程的操作对于初学者而言是比较难理解的,其实联想到底层操作系统时我们可能会稍微明白些,对于程序而言最终都是硬件上运行二进制指令,然而,这些又太过底层,今天来看一下JAVA中的线程,浅析JD ...
- 曹工说JDK源码(2)--ConcurrentHashMap的多线程扩容,说白了,就是分段取任务
前言 先预先说明,我这边jdk的代码版本为1.8.0_11,同时,因为我直接在本地jdk源码上进行了部分修改.调试,所以,导致大家看到的我这边贴的代码,和大家的不太一样. 不过,我对源码进行修改.重构 ...
- JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue
JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...
- 【并发编程】【JDK源码】CAS与synchronized
线程安全 众所周知,Java是多线程的.但是,Java对多线程的支持其实是一把双刃剑.一旦涉及到多个线程操作共享资源的情况时,处理不好就可能产生线程安全问题.线程安全性可能是非常复杂的,在没有充足的同 ...
- 【并发编程】【JDK源码】J.U.C--AQS (AbstractQueuedSynchronizer)(1/2)
J.U.C实现基础 AQS.非阻塞数据结构和原子变量类(java.util.concurrent.atomic包中的类),concurrent包中的基础类都是使用这种模式来实现的.而concurren ...
- 【并发编程】【JDK源码】J.U.C--AQS 及其同步组件(2/2)
原文:慕课网高并发实战(七)- J.U.C之AQS 在[并发编程][JDK源码]AQS (AbstractQueuedSynchronizer)(1/2)中简要介绍了AQS的概念和基本原理,下面继续对 ...
- JDK源码分析之concurrent包(二) -- 线程池ThreadPoolExecutor
上一篇我们简单描述了Executor框架的结构,本篇正式开始并发包中部分源码的解读. 我们知道,目前主流的商用虚拟机在线程的实现上可能会有所差别.但不管如何实现,在开启和关闭线程时一定会耗费很多CPU ...
- 关于JDK源码:我想聊聊如何更高效地阅读
简介 大家好,我是彤哥,今天我想和大家再聊聊JDK源码的几个问题: 为什么要看JDK源码 JDK源码的阅读顺序 JDK源码的阅读方法 为什么要看JDK源码 一,JDK源码是其它所有源码的基础,看懂了J ...
随机推荐
- springmvc json数据返回前台,中文乱码
@ResponseBody@RequestMapping(value = "selectProvinces",produces = "text/json;charset= ...
- [].slice.call(arguments,1)
[转] 前言 今天偶然翻资料看到一个叫做软绑定的函数,用来确定this的; 原代码 if(!Function.prototype.softBind){ Function.prototype.softB ...
- 解析Linux中的VFS文件系统之文件系统的注册(二)
继上一篇文章:http://www.cnblogs.com/linhaostudy/p/7397024.html 3. 文件系统的注册 这里的文件系统是指可能会被挂载到目录树中的各个实际文件系统,所谓 ...
- js设计模式之惰性单例模式
<html> <body> <button id="loginBtn">登录</button> </body> < ...
- 在HTML页面中获取当前项目根路径的方法
在HTML页面获取项目根路径的方法: function getRootPath(){ var curPageUrl = window.document.location.href; var rootP ...
- Machine Learning - week 1
Matrix 定义及基本运算 Transposing To "transpose" a matrix, swap the rows and columns. We put a &q ...
- TCP 和 UDP
TCP协议与UDP协议的区别 首先咱们弄清楚,TCP协议和UCP协议与TCP/IP协议的联系,很多人犯糊涂了,一直都是说TCP/IP协议与UDP协议的区别,我觉得这是没有从本质上弄清楚网络通信! ...
- p2p项目,自己期望太高了。
在项目的进行中,主要牵扯到了一些安全方面的考虑,跟money相关的嘛,如果安全不考虑,你就惨了,一期呢,为了防止数据被篡改,主要对数据用dsa进行了签名,二期呢,考虑到das产生的字符串有可能太长,修 ...
- seaJS 模块加载过程分析
先看一个seajs的官方example, 以下以seajs.use('main')为例, 解析加载mod main的过程 //app.html seajs.use("main") ...
- 详解String类中的intern()方法
我们用一个经典的例子来理解 package com.jvm.heap; public class MyTest { public static void main(String[] args) { S ...