/*
举例start和run的区别
* */
public class ThreadTest {
private static void attack(){
System.out.println("Fight");
System.out.println("Current Thread is : " + Thread.currentThread().getName());
}
public static void main(String[] args) {
Thread thread = new Thread(){
@Override
public void run() {
attack();
}
};
System.out.println("Current Main Thread is " + Thread.currentThread().getName());
thread.run(); //输出 Current Thread is : main
//thread.start(); //Current Thread is : Thread-0
}
}
调用run的时候,会沿用主线程来执行方法。调用start则会使用非main线程来执行attack方法。
在线查看jdk1.8源码
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/3560e0ebe876
Thread属于java.lang包里
http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/3560e0ebe876/src/share/native/java/lang
查看start源码之后,发现调用的是
private native void start0();
通过查看Thread.c源码,发现start0 调用的是
{"start0", "()V", (void *)&JVM_StartThread},
然后再去查看jvm.cpp源码
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/3ece33697a35/src/share/vm/prims/jvm.cpp
在方法中
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
发现 语句会创建一个新的线程
native_thread = new JavaThread(&thread_entry, sz);
查看 thread_entry方法 中发现 会call虚拟机 并且传入执行方法的名字
最终会创建一个线程 然后去执行run方法里面的内容
static void thread_entry(JavaThread* thread, TRAPS) {
HandleMark hm(THREAD);
Handle obj(THREAD, thread->threadObj());
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result,
obj,
KlassHandle(THREAD, SystemDictionary::Thread_klass()),
vmSymbols::run_method_name(),
vmSymbols::void_method_signature(),
THREAD);
}
Thread是一个类,Runnable是一个接口
查看Thread源码 发现
class Thread implements Runnable
并且 Runnable 接口中只有一行 抽象方法
public abstract void run();
public class MyThread extends Thread {
private String name;
public MyThread(String name){
this.name = name;
}
@Override
public void run() {
for(int i = 0;i < 5; i ++){
System.out.println("Thread start : " + this.name + " i = " + i);
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread thread = new MyThread("Thread1");
thread.start();
}
}
结果
Thread start : Thread1 i = 0
Thread start : Thread1 i = 1
Thread start : Thread1 i = 2
Thread start : Thread1 i = 3
Thread start : Thread1 i = 4
public class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name){
this.name = name;
}
@Override
public void run() {
for(int i = 0;i < 5; i ++){
System.out.println("Thread start : " + this.name + " i = " + i);
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable("Runnable1");
Thread thread = new Thread(runnable);
thread.start();
}
}
结果
Thread start : Runnable1 i = 0
Thread start : Runnable1 i = 1
Thread start : Runnable1 i = 2
Thread start : Runnable1 i = 3
Thread start : Runnable1 i = 4
优点:实现简单。缺点:需要自己实现循环等待的逻辑,当需要等待的变量变多,整个代码块很显得很臃肿,并且不能做到精准控制
优点:比主线程等待法更精准的控制,并且实现更简单。缺点:力度不够细
使用线程池可以提交多个Callable方法的类,让线程池并发的处理结果,这样可以对Callable的执行方式做统一的管理
public class CycleWait implements Runnable {
private String value;
@Override
public void run() {
try {
Thread.currentThread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
value = "我是value,我现在有数据了";
}
public static void main(String[] args) {
CycleWait cycleWait = new CycleWait();
Thread thread = new Thread(cycleWait);
thread.start();
//实现处理线程的返回值 方法一 主线程等待法
while (cycleWait.value == null){ //核心代码 循环等待
try {
Thread.currentThread().sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//实现处理线程的返回值 方法二 使用Thread类的join()阻塞当前线程以等待子线程处理完毕
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(cycleWait.value);
}
}
public class MyCallable implements Callable {
@Override
public String call() throws Exception {
String value = "test";
System.out.println("开始工作");
Thread.currentThread().sleep(5000);
System.out.println("任务完成");
return value;
}
}
//方式1 通过FutureTask获取
public class FutureTasDemo {
public static void main(String[] args) {
FutureTask task = new FutureTask<>(new MyCallable());
new Thread(task).start();
if(!task.isDone()){
System.out.println("任务还没做完,再等待");
}
try {
System.out.println("返回值是:" + task.get());
} catch (Exception e) {
e.printStackTrace();
}
}
}
//方式2 通过线程池获取
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
Future future = newCachedThreadPool.submit(new MyCallable());
if(!future.isDone()){
System.out.println("还没做完,再等待");
}
try {
System.out.println("返回值是" + future.get());
} catch (Exception e) {
e.printStackTrace();
} finally {
newCachedThreadPool.shutdown();
}
}
}
public static enum ThreadState
extends Enum
A thread state. A thread can be in one of the following states:
NEW
A thread that has not yet started is in this state.
RUNNABLE
A thread executing in the Java virtual machine is in this state.
BLOCKED
A thread that is blocked waiting for a monitor lock is in this state.
WAITING
A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
TIMED WAITING
A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
TERMINATED
A thread that has exited is in this state.
A thread can be in only one state at a given point in time. These states are virtual machine states which do not reflect any operating system thread states
公共静态枚举线程状态
扩展枚举
线程状态。线程可以处于以下状态之一:
新建
尚未启动的线程处于此状态。
可运行的
Java虚拟机中执行的线程处于此状态。
阻塞
等待监视器锁定被阻止的线程处于此状态。
等待
无限期等待另一线程执行特定操作的线程处于此状态。
定时等待
在指定等待时间内等待另一线程执行操作的线程处于此状态。
结束
已退出的线程处于此状态。
一个线程只能在给定的时间点处于一个状态。这些状态是不反映任何操作系统线程状态的虚拟机状态
以下方法都会使线程进入Waiting状态
没有设置Timeout参数的Object.wait()方法
没有设置Timeout参数的Thread.join()方法
LockSupport.part()方法
以下方法会使线程进入Time Waiting状态
Thread.sleep()方法
设置了Timeout参数的Object.wait()方法
设置了Timeout参数的Thread.join()方法
LockSupport.parkNanos()方法
LockSupport.parkUntill()方法
thread.join(); thread.start(); 调用join方法之后再调用start方法会抛出 IllegalThreadStateException 异常。已经结束的线程不能在复活了
通过实现Runable接口或者继承 Thread类 可以创建一个线程
新建——>可运行
调用线程的start方法,线程就会进入到 可运行(runable) 状态
可运行——>运行
可运行状态的线程 被OS选中 并且获得时间片之后 就会进入到运行(running)状态
运行——>可运行
如果运行(running)状态 调用yield 方法 会让出CPU 回到 可运行(runnable)状态 (取决于操作系统的调度 yield只是起到建议的作用)
如果时间片已经用完 运行状态 也会进入到 可运行状态
运行——>阻塞
如果处于运行状态的线程,如果要等待用户的输入I/O输入,或者调用sleep()或者join()方法 会进入到阻塞的状态。此时会让出CPU。如果当前线程获得锁,上述情况并不会对锁的占用会有任何影响。即不会释放已经获得的锁。
运行,可运行——>阻塞
处于 运行(running)状态 或者 可运行(runnable) 状态的线程,执行 synchronized 方法 或者 synchronized 方法块的时候。发现并未获取到相应的锁,也会进入到阻塞状态。同时会被放入对象的锁池当中。
运行——>等待队列
如果处于运行(running)状态的线程,调用wait方法。就会进入限期或者非限期等待状态。同时还会被放入锁对象的等待队列当中。
等待队列——>锁池
如果处于等待队列中的线程,wait时间到或者被其他线程调用notify 或者 notifyAll 唤醒的话,就会被放入锁池当中。
锁池——>可运行
处于锁池当中的线程获得了锁,就会转为可运行(runnable)当中。
可运行——>运行
等待OS选中后,就会回到运行(runable)状态。
运行——>死亡
最后处于运行(running状态)线程,等待方法执行完毕或者异常退出之后。线程就会结束,进入死亡状态。
public class WaitSleepDemo {
/* lock.wait(1000); 获取同步锁只会 会wait 1s 在这段时间
* 第二个线程(sleep)开始执行
* 如果线程1没有释放lock 线程2就会被进入阻塞状态 不能执行线程2 synchronized 里面的逻辑
* 如果wait可以释放锁 那么 线程2 synchronized 将会被执行
* 如果将sleep放在线程1 而 wait放在线程2 那么线程2 synchronized 的代码不会被执行*/
public static void main(String[] args) {
final Object lock = new Object();
//wait逻辑
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程 A 正在等待得到lock");
synchronized (lock){
try {
System.out.println("线程 A 得到lock");
Thread.sleep(20);
System.out.println("线程 A 等待方法");
lock.wait(1000); //如果不传入时间 线程就会进入Waiting(无限期等待)状态
System.out.println("线程 A 已经执行完成");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}).start();
//使wait方法先执行
try{
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
//sleep逻辑
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程 B 正在等待得到lock");
synchronized (lock){
try {
System.out.println("线程 B 得到lock");
System.out.println("线程 B 休眠 10 ms");
Thread.sleep(10);
System.out.println("线程 B 已经执行完成");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}).start();
}
}
结果
线程 A 正在等待得到lock
线程 A 得到lock
线程 B 正在等待得到lock
线程 A 等待方法
线程 B 得到lock
线程 B 休眠 10 ms
线程 B 已经执行完成
线程 A 已经执行完成
当线程进入到 无限等待状态(Waiting) 时 可以使用notyfy和notyfyAll 唤醒线程
了解两个概念
假设线程A已经拥有了某个对象(不是类)的锁,而其它线程B、C想要调用这个对象的某个synchronized方法(或者块),由于B、C线程在进入对象的synchronized方法(或者块)之前必须先获得该对象锁的拥有权,而恰巧该对象的锁目前正被线程A所占用,此时B、C线程就会被阻塞,进入一个地方去等待锁的释放,这个地方便是该对象的锁池
假设线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁,同时线程A就进入到了该对象的等待池中,进入到等待池中的线程不会去竞争该对象的锁。
没有获取到锁,或者已经呆在锁池中的只能等待其他机会去获取锁,而不能主动回到等待池当中
JDK介绍yield
A hint to the scheduler that the current thread is willing to yield its current use of a processor. The scheduler is free to ignore this hint.
向调度程序提示当前线程愿意放弃当前对处理器的使用。调度程序可以忽略此提示。
yield并不会使线程让出锁
public class YieldDemo {
public static void main(String[] args) {
Runnable yieldTask = new Runnable() {
@Override
public void run() {
for(int i = 1; i <= 10; i ++){
System.out.println(Thread.currentThread().getName() + i);
if(i == 5){
Thread.yield();
}
}
}
};
Thread threadA = new Thread(yieldTask,"A");
Thread threadB = new Thread(yieldTask,"B");
threadA.start();
threadB.start();
}
}
这个方法太过于暴力,并且不安全。比如说线程A调用线程B的stop方法,去停止线程B。调用这个方法的时候,线程A并不知道线程B执行的具体情况,这种突然的停止,会导致线程B的清理工作无法完成。还有一种情况,执行stop方法之后线程B,会马上释放锁,会有可能引发数据不同步的问题。
1.如果线程处于被阻塞状态,那么线程将立即退出阻塞状态(sleep,wait,join),并抛出y一个InterruptedException异常
2.如果线程处于正常活动状态,那么会将该线程的中断标志设置为true。被设置中断标志饿线程将继续正常运行,不受影响。
1.在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程
2.如果线程处于正常活动的状态,那么会将该线程的中断标志设置为true。被设置中断标志的线程将继续正常运行,不受影响
public class InterruptDemo {
public static void main(String[] args) throws InterruptedException {
Runnable interruptTask = new Runnable() {
@Override
public void run() {
int i = 0;
try{
//在正常运行任务时,经常检查本线程的中断标志,如果被设置了中断标志j就自行停止线程
while (!Thread.currentThread().isInterrupted()){
Thread.sleep(100);//休眠一会
i ++;
System.out.println(Thread.currentThread().getName() + " (" + Thread.currentThread().getState() + " ) loop " + i);
}
}catch (InterruptedException e){
//在调用阻塞方法时正确处理 InterruptedException 异常。(例如catch 异常后就结束线程)
System.out.println(Thread.currentThread().getName() + " (" + Thread.currentThread().getState() + " ) catch InterruptedException" );
}
}
};
Thread thread1 = new Thread(interruptTask,"A");
System.out.println(thread1.getName() + " (" + thread1.getState() + " ) 已经被创建");
thread1.start();
System.out.println(thread1.getName() + " (" + thread1.getState() + " ) 已经开始运行");
//主线程休眠 然后主线程给thread1 发"中断指令"
Thread.sleep(300);
thread1.interrupt();
System.out.println(thread1.getName() + " (" + thread1.getState() + " ) 发出中断指令");
//主线程休眠 然后查看thread1状态
Thread.sleep(300);
System.out.println(thread1.getName() + " (" + thread1.getState() + " ) 已经被中断");
}
}
结果
A (NEW ) 已经被创建
A (RUNNABLE ) 已经开始运行
A (RUNNABLE ) loop 1
A (RUNNABLE ) loop 2
A (TIMED_WAITING ) 发出中断指令
A (RUNNABLE ) catch InterruptedException
A (TERMINATED ) 已经被中断