1.线程的创建
方法一:继承Thread类,重写run方法
class MyThread01 extends Thread{
@Override
public void run() {//描述了线程要执行什么任务
System.out.println("hello");
}
}
public class Thread_Demo01 {
public static void main(String[] args) {
MyThread01 thread01=new MyThread01();
thread01.start();
}
}
方法二:实现Runnable接口,重写run方法
class MyThread02 implements Runnable{
@Override
public void run() {
System.out.println("hello");
}
}
public class Thread_Demo02 {
public static void main(String[] args) {
Thread thread02=new Thread(new MyThread02());
thread02.start();
}
}
方法三:针对方法一的变形,使用匿名内部类,继承Thread类重写run方法
public class Thread_Demo03 {
public static void main(String[] args) {
Thread thread03 = new Thread() {//创建了Thread的一个匿名子类,同时也创建了这个子类的实例
@Override
public void run() {
System.out.println("hello");
}
};
thread03.start();
}
}
方法四:针对方法二的变形,使用匿名内部类,实现Runnable接口重写run方法
public class Thread_Demo04 {
public static void main(String[] args) {
Thread thread04=new Thread(new Runnable() {//创建了一个匿名类实现Runnable接口,创建了这个类的实例
@Override
public void run() {
System.out.println("hello");
}
});
thread04.start();
}
}
方法五:使用lambda表达式(匿名函数)
public class Thread_Demo05 {
public static void main(String[] args) {
Thread thread05=new Thread(()->{
System.out.println("hello");
});
thread05.start();
}
}
2.Thread的常见构造方法与属性
构造方法:
方法 | 说明 |
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用Runnable对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target,String name) | 使用Runnable对象创建线程对象并且命名 |
Thread(ThreadGroup group,Runnable target) | 线程可以被用来分组管理,分好的组即为线程组 |
属性:
属性 | 获取方法 |
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
3.线程中断
你正在撰写一份报告,突然接到电话,得知有紧急会议需要立刻参加。这时,你需要暂停报告撰写,转而准备会议。这就是一个线程中断的实例:你的“报告撰写线程”被“紧急会议通知”中断,转而执行“参加会议”的任务。线程中断在编程中也是类似的概念,用于在需要时终止或改变线程的执行流程,那么如何实现线程中断呢?
方法一:自己来实现控制线程结束的代码
public class Thread_test01 {
private static boolean flag=true;//使用flag变量来结束线程
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
while(flag) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("hello");
}
System.out.println("t线程结束");
});
t.start();
Thread.sleep(100);
flag=false;
}
}
运行结果如下:
方法二:使用Thread提供的interrupt和interrupted方法
public class Thread_test02 {
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
while(!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("hello");
}
System.out.println("t线程结束");
});
t.start();
Thread.sleep(100);
t.interrupt();
}
}
代码会报错,但是整个线程仍然没有结束,而是继续执行
出现上述情况是因为t线程中的sleep方法,在触发interrupt的时候,线程正在睡眠,sleep被唤醒的同时就会清除刚才的标志位,即把标志位改为false(标志位:类似于第一种方法中的flag变量),之所以要修改标志位,就是把控制权交给程序员自己,用代码来决定线程是否要继续执行,又或者是立刻结束
如果没有sleep方法,那么interrupt方法能够正常使线程终止
4.线程等待--join方法
有时,我们需要等待一个线程结束它的工作后,才能进行自己下一步的工作,那么就需要进行线程之间的等待
join方法的执行有两种可能:
1.如果调用join方法的线程已经结束了,那么join立刻返回
2.如果调用join方法的线程还没有结束,那么join就会阻塞等待,一直等到调用join方法的该线程结束之后,join才能解除阻塞继续执行
public class Thread_join {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 2; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("hello thread1");
}
System.out.println("thread1 end");
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 2; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("hello thread2");
}
System.out.println("thread2 end");
});
t1.start();
t2.start();
t1.join();
t2.join();
for(int i=0;i<2;i++) {
Thread.sleep(1000);
System.out.println("hello main");
}
System.out.println("main end");
}
}
运行结果如下:
main线程调用t1.join(),让main线程阻塞等待t1,接着main线程再调用t2.join(),让main线程阻塞等待t2,t1线程和t2线程都终止后,再执行main线程中的代码,至于t1和t2线程的结束顺序,因为t1和t2是并发执行的,所以t1和t2线程的结束顺序是随机的
main线程调用t1.join()时,与t2线程无关,同样调用t2.join()时,与t1线程无关
不过无参数的join方法会出现死等问题,为了避免死等问题,可以自定义等待时间,这时就可以使用带时间参数的join方法
方法 | 说明 |
public void join() | 等待线程结束 |
public void join(long millis) | 等待线程结束,最多等millis毫秒 |
public void join(long millis,int nanos) | 同理,但可以更高精度 |
以带一个参数的join方法为例:
public class Thread_join01 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 2; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("hello thread1");
}
System.out.println("thread1 end");
});
t1.start();
t1.join(500);
for (int i = 0; i < 2; i++) {
Thread.sleep(1000);
System.out.println("hello main");
}
System.out.println("main end");
}
}
运行结果如下:
main线程在阻塞等待t1线程500毫秒后,开始和t1线程并发执行
5.线程休眠
线程休眠使用的就是上面代码中使用的sleep方法,使用sleep方法能够根据时间参数来阻塞当前进程,但实际休眠时间与参数设置的休眠时间会存在些许偏差
方法 | 说明 |
public static void sleep(long millis) throws InterruptedException | 休眠当前线程millis毫秒 |
public static void sleep(long millis,int nanos) throws InterruptedException | 可以实现更高精度的休眠 |
6.获取线程实例
方法 | 说明 |
public static Thread currentThread() | 返回当前线程对象的引用 |
public class Thread_current { public static void main(String[] args) { Thread thread=Thread.currentThread(); System.out.println(thread.getName()); } }
上述代码中获取的线程对象是main线程,getName方法获取了main线程的名字,所以输出结果是main
7.线程状态
7.1线程状态的种类
1.New:Thread对象有了,还没调用start,系统内部的线程还未创建
2.Terminated:线程已经结束了,内核中的线程已经销毁
3.Runnable(就绪状态):这个线程正在cpu上执行或者这个线程虽然没有在cpu上执行,随时都可以调度到cpu上执行
4.Waiting:死等进入的阻塞
5.Timed_Waiting:带有超时时间的阻塞
6.Blocked:进行锁竞争时产生的阻塞
7.2线程状态之间的切换