线程的创建
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源码解析/