目录
1.线程创建
方法1.继承Thread类:
class MyThread extends Thread{//继承Thread来创建一个线程类
@Override
public void run() {
while(true) {
System.out.println("hello world");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Demo1 {
public static void main(String[] args){
Thread t = new MyThread();//将MyThread实例化
t.start();//真正开始创建线程(在操作系统内核中,创建出对应线程的PCB加入到系统链表中,参与调度)
while(true) {
System.out.println("hello main");//并发+并行,多个线程的执行程序是无序的,是调度器随机调度
try {
Thread.sleep(1000);//线程阻塞,单位为ms
} catch (InterruptedException e) {
e.printStackTrace();//阻塞造成的异常信息
}
}
}
}
方法2.实现Runnable接口
因为Runnable接口也有run方法,所以创建线程实例的同时实现Runnable接口可以调用run方法,做到任务的准备与执行分开
class MyRunnable implements Runnable{//创建一个类实现接口
@Override
public void run() {
while (true) {
System.out.println("hello world");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Demo2 {
public static void main(String[] args) {
Runnable runnable = new MyRunnable();//类的实例化(相当于在准备任务)
Thread s = new Thread(runnable);//线程的实例化(准备执行)
System.out.println("main");
s.start();//新创建的线程开始执行
}
}
方法3.匿名内部类创建Thread对象
public class Demo6 {
public static void main(String[] args) {
Thread t = new Thread() {//此处就是在创建一个匿名的内部类(没有参数)这个类是Thread的子类
//等于继承,方法重写和实例化都在这里完成了
@Override
public void run() {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t.start();
}
}
方法4.匿名内部类创建Runnable子类对象
public class Demo4 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {//中间创建了Runnable子类的对象,相当于中间这些就是构造方法的参数
@Override
public void run() {
while(true){
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}}
方法5:lambda表达式创建Runnable子类对象
public class Demo5 {
public static void main(String[] args) {
Thread t = new Thread(()->{//lambda表达式
while(true){
System.out.println("hello tread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
}
}
线程创建还有其他方式, 比如Callable / FutureTask的方式创建,还有线程池的方式创建,至少有7种
需要注意的是:调用start()方法,才算真正在操作系统的底层创建了一个线程
线程的结束:线程的入口方法执行完,就相当于线程执行完了,例如run()或者main()
2.线程中断
目前共有两种方式:
1.利用共享的标记(全局变量);
2.利用interrupt()方法来通知.
第1种方法示例:
public class Demo11 {
private static boolean isQuit = false;//设置标志位当学到volatile时需要用此关键字来修饰
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while(!isQuit){
System.out.println("hello Thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
});//主线程和新创建的线程其实是同一时刻执行的,所以改变标志位就能退出循环
//System.out.println(t.getState());
t.start();
Thread.sleep(2000);
System.out.println("hello main");
isQuit = true;//需要中断时改变标志位
}
}
2.利用interrupt方法会有两种情况:
1.如果是线程不处于阻塞状态时,使用interrupt会改变标志位;
2.如果是线程处于阻塞状态sleep()时,使用interrupt会让线程内部产生阻塞的办法例如sleep抛出InterruptedException异常执行catch后的语句,这时可以自己控制中断:
一.直接结束执行(break)
二.稍后等一会再结束(sleep或其他收尾工作 再break)
三.不退出(啥都不干,catch执行完后继续执行循环)
而interrupt方法的标志位我们一般用Thread.currentThread().isInterrupted(),默认设为true,表明即将中断
示例:
public class Demo13 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while(!Thread.currentThread().isInterrupted()){interrupt的标志位,注意前面有!
System.out.println("hello Thread");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
System.out.println("hello main");
Thread.sleep(20000);//这里我故意设置了主线程的休眠时间更长,从而使得创建的线程在interrupt执行时不是处于阻塞的状态,从而得出结果会改变标志位,跳出循环结束,而不会报出程序阻塞时的异常
t.interrupt();
}
}
public class Demo13 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while(!Thread.currentThread().isInterrupted()){
System.out.println("hello Thread");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(500);//这里我设置了主线程时间更短,然后就可以认为创建的线程处于阻塞的状态,从而interrupt方法会抛出InterruptException异常,但循环不会结束,需要我们手动设置
System.out.println("hello main");
t.interrupt();
}
3.线程等待
利用join()方法,如果主线程main需要等待创建的新线程t执行完后才能继续执行,这中间可以加入一个t.join()语句,表示main线程要等待t的线程执行完,就是t必须执行完自己的线程,main才能往后
示例:以计时更直观的看出哪个线程的结束
public class Demo13 {
public static void main(String[] args) {
int COUNT = 10_000;
long begin = System.currentTimeMillis();
Thread t1 = new Thread(()-> {
int a = 0;
for (long i = 0; i < COUNT; i++) {
a++;
}
});
Thread t2 = new Thread(()->{
int b = 0;
for (long i = 0; i < COUNT; i++){
b++;
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
System.out.println("t1.t2运行结束");
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("main运行");
System.out.println("运行时间:"+(end - begin) + "ms");
}
}
此处的意思是需要等到t1.t2线程都运行完了之后main线程才能结束
总结就是想让谁阻塞,谁就调用join.
调用t1.join,main会在t1.join这个语句中阻塞,等待t1完成后再往下走
4.线程休眠
public static void sleep(long millis) throws InterruptedException | 休眠当前线程 millis 毫秒 |
public static void sleep(long millis, int nanos) throws InterruptedException | 可以更高精度的休眠 |
public static void main(String[] args) throws InterruptedException {
long a = System.currentTimeMillis();
Thread.sleep(3 * 1000);
long b = System.currentTimeMillis();
System.out.println("休眠时间为:"+(b-a));
}
5.获取线程实例
线程在Java有个专门的类Thread,里面有其成员属性,成员方法以及构造方法
属性 | 获取方法 |
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
当我们创建了一个线程类对象时,可以利用获取的方法得到线程对象里面的信息