1. 线程创建
其实,在线程的创建中有五种的创建方法,分别为: 1. 继承Thread的类 2. 实现Runnable的接口 3. 继承Thread 使用匿名内部类 4. 实现Runnable 使用匿名内部类 5. 使用lambda表达式
方法 | 说明 |
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target,String name) | 使用Runnable 对象创建线程 并命名 |
Thread(ThreadGroup group,Runnable target) | 线程可以被用来分组管理,分好的组为线程组,这个目前了解就好啦!!! |
在这里我都使创建线程完成run方法的重写,并且每个线程我让他死循环可以更好的观察线程之间的运转过程。还有启动别的线程我们这里使用的是(列如第一个方法:t.start)这里就启动了线程。
1.1 继承Thread的类
class MyThread extends Thread{
@Override
//为什么这里try...catch不能往上抛 因为它继承父类所以不能往上抛
public void run() {
while(true){
System.out.println("hello run");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//用继承Thread来实现run方法
public class ThreadDemo1 {
public static void main(String[] args) {
Thread t = new MyThread();
//用start来启动线程
t.start();
// 让main线程也进入死循环看 线程之间的优先关系
//为什么main线程可以往上抛 因为main没有上述关系
while(true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
//这里中 默认为前台 前台不结束它不结束
}
}
1.2 实现Runnable
class MyThread2 implements Runnable{
@Override
public void run() {
while(true){
System.out.println("hello runnable");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
//实现Runnable接口来完成 run的重写
public class ThreadDemo2 {
public static void main(String[] args) {
Thread t = new Thread(new MyThread2());
t.start();
while(true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
1.3 继承Thread 使用匿名内部类
public class ThreadDemo3 {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
while(true){
System.out.println("hello run");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
t.start();
while(true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
1.4 实现Runnable 使用匿名内部类
public class ThreadDemo4 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
while(true){
System.out.println("hello runnable");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
});
t.start();
while(true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
1.5 使用lambda表达式
public class ThreadDemo5 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while(true){
System.out.println("hello lambda");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
while(true){
System.out.println("hello main");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
其实在上面的五种重写run方法中,最常用的是第五种方法(lambda表达式)也是看起来比较简洁,比较舒服的(千万小心3,4,5的方法中匿名内部类调用对象要小心哦!!!)。
2. 线程的中断
在这里线程的中断我们分为俩个办法:1. ⼿动设置⼀个标志位, 在线程中判断这个标志位的条件, 从⽽决定是否中断 2. 通过interrupted()方法
2.1 手动设置标位
public class ThreadDemo7 {
private static boolean isQuit = false;
public static void main(String[] args) {
Thread t = new Thread(() -> {
while(!isQuit){
System.out.println("hello run");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("t结束了");
});
t.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("让t线程结束");
isQuit = true;
}
}
2.2 interrupted()方法
这里有要抛异常的解释
public class ThreadDemo8 {
public static void main(String[] args) {
Thread t = new Thread(() ->{
while(!Thread.currentThread().isInterrupted()){
System.out.println("hello run");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;//在这里必须要使它 break 因为sleep可能当时在睡觉结果你 把它唤醒了 所以就会报错 因此在捕获异常的时候需要跳出
// 在正常情况下 并不是写 break 而是写一些更重要的东西
}
}
System.out.println("t线程结束");
});
t.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("让t线程结束");
t.interrupt();
}
}
注意点:
这里在try...catch中如果写的是e.printStackTrace(),这里就会出现上面的错误,那是为什么??如果没有sleep,interrupt可以让线程顺利结束。但是有了sleep就有了变数了。在执行sleep的过程中,调用interrupt()大概率sleep的休眠时间还没到,被提前唤醒了,他就会做出俩件事:1.抛出InterruptedException(让catch去获取) 2. 清除Thread对象的isInterrupted标志位。因此想要线程结束加上break就行了,所以上述代码就执行了该方法。
这里讲讲更细的,其实通过interrupt()方法,已经把标志设置为true了,但是sleep提前唤醒操作,就把标志位又设置回false,所以循环不会停止,在工作时catch语句中,会加入一些方法列如:1. 尝试自动恢复 2.记录日志 3. 发出警报 4. 也有少数的正常业务逻辑,会依赖catch等等
3. 线程等待
public class TreadDemo9 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
for(int i = 0; i < 5; i++){
System.out.println("hello run");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
//让线程等待 可以理解为我在main线程中 需要等待t现场结束的时候才继续进行 main线程
t.join();
System.out.println("haha");
}
}
在这里我们主要使用的是join()方法,执行join的时候,就看t线程是否正在运行,1.如果t运行中,main线程就会阻塞 2.如果t线程运行结束,main线程就会从阻塞恢复过来,并且继续往下执行。
4. 线程休眠
让当前的线程休息⼀会, 必须要指定时间的, 防⽌⼀直⽆休⽌的休 眠 在操作系统⾥有多个链表⽤来管理PCB⼀个是就绪队列, 就绪队列中的PCB中以随时调度到CPU上去执⾏⼀个是阻塞队列, 阻塞队列中的PCB要等到休眠时间到了才会被移 动就就绪队列 移回就绪队列并不代码就⽴即可以上CPU执⾏,还是要等CPU的调 度 sleep(1000)真的是休眠1000毫秒吗? 不⼀定, ⽽是说要休眠⾄少 1000毫秒, 1000毫秒过后可能⽴⻢1ms之内,也可能慢⼀点10ms这些系统级别的开销也和操作系统本身有关, 都是不可预期的等待的时候⼀般在公司的项⽬⾥都是需要加上等待时间的,如果没有时间,就可以造成⼀直死等的现象,那么就会影响整个系统的执⾏再⽐如⼀个客户端去访问⽹站, ⼀般也是会加上⼀个等待的超时时 间,⽐如30秒,如果30秒之后服务器还没有返回,就会给⽤户报⼀个 ⽹站不畅的错, 提示让⽤户检查⼀下⽹络, 这样做的⽬的就是能让 程序从错误中恢复出来, 否则程序会⼀直卡在那⾥, 造成⾮常差的⽤户体验.
5. 线程的获取
Thread tt = Thread.currentThread();
线程的获取其实非常的简单就可以通过currentThread()来获取线程。