1. 线程创建
首先需要知道 需要通过继承Thread类来重写thread类的run方法, 并且查看Thread 类源码发现 该Thread 类继承了Runnable 而核心的run()方法正是Runnable 类的 因此衍生出以下创建线程的方法;
通过创建Thread 的子类实现
class MyThread extends Thread{
@Override
public void run() {
System.out.println("继承Thread 实现 创建线程");
}
}
再通过向上转型 接受Thead的子类并调用start方法
public static void main(String[] args) {
//void 1:
Thread t = new MyThread();
t.start();
}
此时就有人要问了, 为什么不直接调用run方法而是调用start方法?
我们需要先知道run方法和start方法的区别;
run方法只是我们重写Thread类的一个普通方法 , 如果直接使用 只相当于调用了t线程的一次run方法,并没有创建一个线程
start方法里面 将run方法作为一个任务,并创建了一个线程让该线程执行这个任务 , 因此 该线程创建完之后相当于与主线程(main)是并发执行的.
通过创建Thread 的匿名内部类
public static void main(String[] args) {
Thread t3 = new Thread(){
@Override
public void run() {
System.out.println("匿名内部类继承 Thread 创建线程");
}
};
t3.start();
}
通过创建Runnable接口的实现类
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("实现Runnable 创建线程 ");
}
}
与Thread创建子类同理, 向上转型后直接调用start()方法;
public static void main(String[] args) {
//void 2:
Thread t2 = new Thread(new MyRunnable());
t2.start();
}
通过创建Runnbale 接口的匿名内部类
public static void main(String[] args) {
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类 实现Runnable 创建线程");
}
});
t4.start();
}
由第四个方法衍生lambda表达式写法
public static void main(String[] args) {
Thread t5 = new Thread(() -> {
System.out.println("lambda 表达式 实现Runnable接口 创建线程");
});
t5.start();
}
此时就有人会看出问题, Thread 和 Runnable 都差不多 , 我到底应该选择哪个去创建线程?
这里我推荐使用Runnable来创建线程, 因为Java中是单继承,如果继承了Thread 就受到了局限性,
如果去实现Runnable接口 就没有该问题.
2. 线程中断
在线程中 存在判断是否为中断的方法为Thread.currentThread.isInterrupted( );
线程的中断方法为 interrupt( );
public static void main(String[] args) {
Thread.interrupted();//静态方法
// 实例方法,其中currentThread 能够获取当前线程的实例
Thread.currentThread().isInterrupted();
Thread t2 = new Thread(() -> {
//判断是否被设置标志位置了
while (!Thread.currentThread().isInterrupted()){
System.out.println("hello thread2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//这里就是被打断了之后立就不执行了
System.out.println("收尾工作...");
break;
}
}
});
t2.start();
//在主线程中,调用interrupt方法,来中断这个线程
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//谁调用这个方法 的意思就是让线程 中断!
//调用这个方法 可能会出现两种情况:
//1) 如果t线程是处在就绪状态,就是设置线程标志位为true
//2) 如果t是一个阻塞状态 Sleep 那么就会触发一个InterrputException
t2.interrupt();
System.out.println("终止 t2 线程!");
}
以上程序会捕捉到一个中断异常, 因为 sleep状态下 线程被打断会抛出一个打断异常. 因此我们捕捉到中断异常就意味着线程中断.
这里是因为如果不加sleep休眠的话,线程会执行非常的快 , 很难看出中断结果, 实际上当线程被中断后, Thread.currentThread().isInterrupted(); 标志位就会变成true 从而中断线程 而不是 通过捕捉中断异常而判断中断线程.
3. 线程等待
Thread.join();方法有几个版本
无参数版本
相当于哪个线程调用这个方法,该线程要等待该线程一直到该线程结束,才继续往下执行操作.
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
for (int i = 0 ; i < 3; i++) {
System.out.println("等等我 , 工作完找你!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//mian线程等待
t.join();
System.out.println("终于等到了,快点结束吧!");
System.out.println("main线程结束");
}
有参数版本
指定等待时间, 如果没有等到那就不再等待,继续执行下面的代码.
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
for (int i = 0 ; i < 3; i++) {
System.out.println("等等我 , 工作完找你!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//mian线程等待 2秒后 如果没结束就直接自己结束!
t.join(2000);
System.out.println("来不及了,不等了!");
System.out.println("main线程结束");
}
4. 线程休眠
Thread.sleep 静态方法
前面已经使用过sleep的用法 , 当一个线程调用Thread.sleep,该线程就会休眠指定的秒数
例如:让线程休眠五次
public static void main(String[] args) {
Thread t = new Thread(() -> {
for (int i = 0; i < 5; i++) {
try {
System.out.println("线程进入休眠" + i + "次");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
5. 获取线程实例
通过使用Thred.currentThread.getName方法获取当前线程的名字
public static void main(String[] args) {
Thread t = new Thread("线程1"){
@Override
public void run() {
//获取thread 的实例对象的名字
System.out.println(Thread.currentThread().getName());
System.out.println(this.getName());
}
};
t.start();
}
此时发现 通过this.getName 调用该类的获取名字的方法 和 获取当前线程名字的方法 Thred.currentThread.getName 是同样的效果 , 那么两者之间有什么区别呢?
this.getName 和 Thred.currentThread.getName 两者的区别是:
如果是Thread类的子类或者匿名内部类的话
this.getName 是可以获取到名字的.
如果是Runnable类型的, 就无法获取到名字,
因为Runnable接口没有getName方法 ,此时this 找不到getName方法 所以就会报红
因此, 当我们获取线程的实例名字的时候, 最好还是使用Thread.currentThread.getName方法;