目录
一、thread的常见的创建方法
1、第一种是在进程里面用Thread类直接重写run方法:
ublic static void main(String[] args) throws InterruptedException {
//第一种写法,run直接写在Thread并且用{}包含
Thread t=new Thread(){
@Override
public void run() {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
t.start();
}
2、第二种是创建一个新的类来继承Thread然后直接实例化再调用:
class MyThread extends Thread{
@Override
public void run() {
while(true){
System.out.println("hello Thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class ThreadDemo1 {
public static void main(String[] args) throws InterruptedException {
//创建线程办法之一——继承thread
Thread t=new MyThread();
t.start();
}
}
3、第三种是先创建Runnable类,然后把Runnable作为参数写进Thread里面,如下:
public static void main(String[] args) throws InterruptedException {
//第三种线程写法,写在Runnable里面,外面调用
Runnable runnable=new Runnable() {
@Override
public void run() {
while(true){
System.out.println("hello runnable");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
};
Thread t=new Thread(runnable);
t.start();
}
4、第四种写法,和第二种有点大同小异,就是创建一个类来继承Runnable接口,然后作为参数写进Thread里面,如下:
class MyRunnable implements Runnable{
@Override
public void run() {
while(true){
System.out.println("hello runnable");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) throws InterruptedException {
//第二种写线程办法Runnable
Runnable runnable=new MyRunnable();
Thread t=new Thread(runnable);
t.start();
}
}
5、最后一种是匿名内部类的写法,也是用的最多的写法,代码演示如下:
public static void main(String[] args) throws InterruptedException {
//线程的第五种写法,匿名写法
Thread t=new Thread(()->{
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
}
我们能大概了解Thread如何创建,其中Runnable的写法可以更好的解耦合,但用的最多的是匿名内部类的写法,因为这种写法比较方便。
二、Thread的几个常见属性以及如何启动一个线程
1、第一个常见的属性就是ID,在线程中的方法是getId(),顾名思义,id是线程的唯一标识,不同的线程不会重复
2、第二个属性是名称,方法是getName(),可以得到线程的名称
3、第三个属性是状态,方法是getState(),可以得到线程得状态,下列是状态表。
状态名称 | 解释 |
NEW | 安排了工作,还未开始行动 |
RUNNABLE | 可工作的.又可以分成正在工作中和即将开始工作;这几个都表示排队等着其他事情 |
WAITING | 这几个都表示排队等着其他事情 |
TIMED_WAITING | 这几个都表示排队等着其他事情 |
TERMINATED | 工作完成了 |
4、第四个属性是优先级,方法是getPriority(),可以得到线程优先级。
5、第五个属性是判断是否为后台线程(守护线程),即主线程结束就结束,方法为isDaemon();
6、第六个属性是判断是否存活,方法是isAlive(),返回的是boolean值,存活为true,反之false。
7、第七个属性是判断是否被中断,方法为isInterrupted(),返回得也是Boolean值
8、启动一个线程是start。
public static void main(String[] args) {
Thread t1=new Thread(()->{
for (int i = 0; i < 3; i++) {
System.out.println("em");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t1.start();
System.out.println(t1.getId());
System.out.println(t1.getName());
System.out.println(t1.getState());
System.out.println(t1.getPriority());
System.out.println(t1.isDaemon());
System.out.println(t1.isAlive());
System.out.println(t1.isInterrupted());
}
结果如下:
三、线程的常用方法
1、中断线程
中断线程的方法一般使用线程中interrupt的方法中断,具体案例如下:
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
while (!Thread.currentThread().isInterrupted()){
System.out.println("hello world");
try {
//线程每次执行都阻塞1秒
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
break;
//加上break就是立刻终止
//不写东西就是不终止
//catch种先执行一些其他逻辑再break,就是稍后终止
}
}
System.out.println("t结束");
});
t.start();
Thread.sleep(3000);
System.out.println("main线程尝试终止t线程");
t.interrupt();
System.out.println(t.isInterrupted());
}
打印结果如下:
三秒后线程被中断,线程也就结束了。
2、等待线程
等待线程我们用的是join的方法,当要注意的是,join有三种用法:第一种是没有参数的,也就是要等待这个线程结束,第二种是只有一个参数的,就是等待这个线程,最多等多少毫秒,第三种是俩个参数的,比较少用,也是等待线程最多时间,第一个参数是毫秒,第二个参数是纳秒。
我们以俩个参数作为例子。
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
for (int i = 0; i < 3000; i++) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("t线程结束");
});
t.start();
t.join(3000,500);//第一个参数是毫秒,第二个参数是纳秒
System.out.println("main线程结束");
}
打印结果如下:
在等待了3秒几的时候主线程就继续执行了,不再等待了,而如果没有参数,那么主线程就需要继续等待3000多秒才能结束。
3、获取当前线程引用
获取当前线程的引用用的是currentThread的方法,然后可以获取该线程的名字。
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
打印结果如下:
我们可以知道该线程的引用是main。
4、休眠当前线程
休眠线程一般用俩种,第一种是sleep()方法,第二种是wait/notify方法,第一种在前面代码中有解释,sleep里面的参数为多少毫秒,而第二种wait(),wait分为三类,第一类是无参数,需要靠notify唤醒,第二类为一个参数,多少之后自己被唤醒,参数单位为毫秒,第三类为俩个参数,也是自己被唤醒,第二个参数也是纳秒,因为第二类和第三类和sleep有点像,我们重点说一下第一类。
public class ThreadDemo24waitnotify {
public static void main(String[] args) {
Object locker=new Object();
Object locker2=new Object();
Thread t1=new Thread(()->{
try {
Thread.sleep(3000);
System.out.println("wait之前");
synchronized (locker){
locker.wait();
}
System.out.println("wait之后");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
Thread t2=new Thread(()->{
Scanner scanner=new Scanner(System.in);
System.out.println("输入任意内容");
scanner.next();
synchronized (locker){
locker.notify();
}
});
t1.start();
t2.start();
}
}
打印结果如下:
在调用wait方法的时候,线程进入阻塞状态,在写内容之后执行notify唤醒,就可以继续执行。
注意:如果有多个线程使用同一个锁,那么notify只能唤醒其中一个,notifyall才能唤醒全部。