创建线程的三种方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class ThreadDemo extends Thread { // 1. 新建一个类继承 Thread 类,并重写 Thread 类的 run() 方法。 @Override public void run() { System.out.println("Hello Thread"); } public static void main(String[] args) { // 2. 创建 Thread 子类的实例。 ThreadDemo threadDemo = new ThreadDemo(); // 3. 调用该子类实例的 start() 方法启动该线程。 threadDemo.start(); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class RunnableDemo implements Runnable {
// 1. 创建一个类实现 Runnable 接口,并重写该接口的 run() 方法。 @Override public void run() { System.out.println("Hello Runnable"); }
public static void main(String[] args) { // 2. 创建该实现类的实例。 RunnableDemo runnableDemo = new RunnableDemo(); // 3. 将该实例传入 Thread(Runnable r) 构造方法中创建 Thread 实例。 Thread thread = new Thread(runnableDemo); // 4. 调用该 Thread 线程对象的 start() 方法。 thread.start(); } }
|
该方式可以获取线程执行的结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| public class CallableDemo implements Callable<String> {
// 1. 创建一个类实现 Callable 接口,并重写 call() 方法。 @Override public String call() throws Exception { System.out.println("CallableDemo is Running"); return "Hello Callable"; } public static void main(String[] args) { // 2. 创建该 Callable 接口实现类的实例。 CallableDemo callableDemo = new CallableDemo(); // 3. 将 Callable 的实现类实例传入 FutureTask(Callable<V> callable) 构造方法中创建 FutureTask 实例。 FutureTask<String> futureTask = new FutureTask<>(callableDemo); // 4. 将 FutureTask 实例传入 Thread(Runnable r) 构造方法中创建 Thread 实例。 Thread thread = new Thread(futureTask); // 5. 调用该 Thread 线程对象的 start() 方法。 thread.start(); // 6. 调用 FutureTask 实例对象的 get() 方法获取返回值。 // 该方法阻塞直到子线程结束返回结果 try { System.out.println(futureTask.get()); } catch (Exception e) { e.printStackTrace(); } } }
|
线程的三种状态
- 就绪状态:调用线程的start()方法后,处于就绪状态,随时等待cpu调度执行
- 运行状态:cpu调度执行run方法
- 阻塞状态:执行的cpu时间片到了,线程被cpu调度暂时挂起,调度其他线程执行
线程优先级
1 2 3 4 5 6 7 8 9
| //设置优先级 public final void setPriority(int newPriority) //获取优先级 public final int getPriority()
//Thread类的三个静态变量 MAX_PRIORITY:优先级为 10 NORM_PRIORITY:优先级为 5 MIN_PRIORITY:优先级为 1
|
线程优先级为1-10范围内
后台线程/守护线程
1 2 3 4
| //将线程设置为后台线程,参数true public final void setDaemon(boolean on) //返回此线程是否为后台线程 public final boolean isDaemon()
|
后台线程的特点就是当前台线程全部结束后,后台线程就会随之结束。
它的唯一用途就是为其他线程提供服务,如:计时器线程,定时的向其他线程发送信号或清空过时的高速缓存项线程。当程序只剩下守护线程时,JVM就退出了,因为只剩它守护线程了,没有服务对象了,也就没有再继续运行的必要了。
守护线程不应该去访问固定资源,如:文件,数据库,因为它不定在什么时候被中断,而导致资源未关闭。
native方法
native关键字说明它修饰的是一个原生态方法,它的实现不是在当前文件,而是采用其他语言编写的(如C, C++等)。
Java语言本身不能对操作系统底层方法进行访问和调用,而C,C++等语言可以,因为操作系统是采用这些语言实现的。但Java可以通过JNI接口调用其他语言来实现对底层进行访问。
Thread.currentThread()
Thread.State getState()
- 得到实例线程的当前状态,Thread.State枚举值参考JDK API
Thread.sleep()
1 2 3
| //Java原生方法,实现采用其他语言实现 public static native void sleep(long millis) public static void sleep(long millis, int nanos)
|
注意为静态方法,让当前线程从运行状态到阻塞状态,参数为阻塞的时间,millis为毫秒,nanos为纳秒
yield()
1 2
| //Java原生方法,实现采用其他语言实现 public static native void yield();
|
理论上,yield意味着放手,放弃,投降。
一个调用yield()方法的线程告诉JVM它乐意让其他同优先级线程或比它优先级高的线程占用自己的位置。即让当前线程从运行状态到就绪状态,而不是阻塞状态,稍后可能会继续被调度执行。
这表明该线程没有在做一些紧急的事情。注意,这仅是一个暗示,不能保证使得当前正在运行的线程迅速转换到可运行的状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class YieldDemo extends Thread {
@Override public void run() {
for (int i = 0; i < 50; i++) { System.out.println(getName() + " " + i);
//i=20时,当前线程主动让出cpu执行权, if (i == 20) { Thread.yield(); } } }
public static void main(String[] args) {
YieldDemo yieldDemo1 = new YieldDemo(); YieldDemo yieldDemo2 = new YieldDemo();
yieldDemo1.start(); yieldDemo2.start(); } }
|
join()
1 2 3
| public final void join() throws InterruptedException public final synchronized void join(long millis) throws InterruptedException
|
这个方法要有两个线程,也就是一个线程t1的线程体中有另一个线程t2调用join()方法,即t1的run方法中有t2.join()调用,此时t1会被阻塞,直到t2线程执行完,或join(millis)时间到,才会继续执行t1的run方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class JoinDemo extends Thread { //子线程任务 @Override public void run() { for(int i = 0; i < 50; i++) { System.out.println(getName() + " " + i); } } public static void main(String[] args) throws Exception { JoinDemo joinDemo = new JoinDemo(); for(int i = 0; i < 50; i++) { //主线程运行到i=20时被阻塞,开始执行50次子线程,然后继续执行主线程 if(i == 20) { joinDemo.start(); joinDemo.join(); } System.out.println(Thread.currentThread().getName() + " " + i); } } }
|
sleep()和yield()区别
作用处 |
sleep() |
yield() |
给其他线程执行机会 |
会给其他线程执行机会,不会理会其他线程的优先级 |
只会给优先级相同,或者优先级更高的线程执行机会 |
影响当前线程的状态 |
从阻塞到就绪状态 |
直接进入就绪状态 |
异常 |
需要抛出 InterruptedException |
不需要抛出任何异常 |
Tread Dump(线程转储)
- 线程转储是一个JVM中活动线程的列表
- 它对分析系统的性能瓶颈和死锁非常有用
分析工具
- 有很多方法可以获取线程转储——使用Profiler,Kill -3命令,jstack工具等等
- jstack工具,因为它容易使用并且是JDK自带的
- 由于它是一个基于终端的工具,所以我们可以编写一些脚本去定时的产生线程转储以待分析