线程创建
方法一:用一个类 继承Thread 重写run方法
//创建一个类 继承Thread class MyThread extends Thread { //run方法是线程的入口 @Override public void run() { while (true){ System.out.println("hello Thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
这样一个线程就创建好了,然后可以创建MyThread实例。
public class demo1 { public static void main(String[] args) throws InterruptedException { Thread thread = new MyThread();//向上转型 也可以写成MyThread myThread = new MyThread(); //运行线程 thread.start(); while (true){ System.out.println("hello main"); //每1000毫秒打印一次 也就是休眠一秒 Thread.sleep(1000); } } }
方法二:实现 Runnable 接口 重写run方法
class MyRunnable implements Runnable { @Override public void run() { while (true){ System.out.println("hello Runnable"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
我们可以通过实现Runnable接口来实现创建线程
public class demo2 { public static void main(String[] args) throws InterruptedException { //runnable表示的是一个可运行的任务 这个任务是交给线程来负责执行的 Runnable runnable = new MyRunnable(); Thread t = new Thread(runnable); t.start(); while (true){ System.out.println("jello main"); Thread.sleep(1000); } } }
方法三:使用匿名内部类,继承Thread 重写run方法
public class demo3 { public static void main(String[] args) throws InterruptedException { //使用匿名类创建 Thread 子类对象 Thread t = new Thread(){ @Override public void run() { while (true){ System.out.println("thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } } } }; t.start(); while (true){ System.out.println("main"); Thread.sleep(1000); } } }
方法四:使用匿名内部类,实现Runnable 重写run方法
public class demo4 { public static void main(String[] args) { //使用匿名内部类创建 Runnable 子类对象 Thread t = new Thread(new Runnable() { @Override public void run() { while (true){ System.out.println("thread"); } } }); t.start(); while (true){ System.out.println("main"); } } }
方法五:使用 lambda 表达式创建 Runnable 子类对象
public class demo5 { public static void main(String[] args) { //lambda 表达式创建 Runnable 子类对象 //lambda 表达式本质上是一个匿名函数 Thread t = new Thread(()->{ while (true){ System.out.println("thread"); } }); t.setDaemon(true); t.start(); } }
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
创建好线程之后我们可以运行程序观察线程,多线程运行的时候我们可以使用jdk的bin目录下的 jconsole.exe 来观察该进程里多线程的情况。如果运行 jconsole.exe 什么都不显示,就需要以管理员的方式运行。
Thread类的常见构造方法
方法 | 说明 |
Thread() | 创建线程对象 |
Thread(Runnable target) | 使用 Runnable 对象创建线程对象 |
Thread(String name) | 创建线程对象,并命名 |
Thread(Runnable target, String name) | 使用 Runnable 对象创建线程对象,并命名 |
**Thread 的几个常见属性 **
属性 | 获取方法 |
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否为后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
- ID 是线程的唯一标识,不同线程不会重复
- 状态就是表示线程当前所处的一个情况
- 优先级高的线程理论上来说更容易被调度到
- 关于后台线程也可以叫守护线程,后台线程不结束也不会影响整个线程的结束,但是如果一个前台线程没有执行结束,那么整个进程也一定不会结束(JVM会在一个进程的所有非后台线程结束后,才会结束运行)
- 是否存活,就是 run 方法是否运行结束
线程中断
在Java里面中断/销毁一个线程其实就是让run尽快执行结束,我们可以在代码中手动创建标志位来作为run的结束条件。
public class demo6 { private static boolean isQuit = false;//设置标志位 public static void main(String[] args) throws InterruptedException { Thread t = new Thread(()->{ System.out.println("线程正在工作"); while (!isQuit){ System.out.println("thread"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("线程工作结束"); }); t.start(); Thread.sleep(5000); isQuit=true; System.out.println("hello"); } }
运行结果
上述方法手动创建标志位,当线程内部sleep的时候,,主线程修改变量,新线程不能及时响应,我们就可以使用Thread.currentThread().isInterrupted() 来代替自定义标志位。Thread 内部包含了一个 boolean 类型的变量作为线程是否被中断的标记 。
方法 | 说明 |
public void interrupt() | 中断对象关联的线程,如果线程正在阻塞,则以异常方式通知, 否则设置标志位 |
public static boolean interrupted() | 判断当前线程的中断标志位是否设置,调用后清除标志位 |
public boolean isInterrupted() | 判断对象关联的线程的标志位是否设置,调用后不清除标志位 |
public class demo7 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(()->{ //Thread类内部 有一个标志位 可以用来判断当前的循环是否要结束 //Thread.currentThread()就是获取当前线程 while (!Thread.currentThread().isInterrupted()){ System.out.println("线程正在工作"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); //break; //加了break后会立即中断 //我们可以加一些其他的代码 然后再break 这样我们可以有更多的操作空间 } } }); t.start(); Thread.sleep(5000); System.out.println("通过标志位使t线程终止"); //interrupt可以使sleep内部出现一个异常 从而使线程被提前唤醒 手动创建出的标志位无法达到这个效果 t.interrupt();//把上述标志位设置为true 即使线程内部出现阻塞 也可以被唤醒 } }
我们观察上面代码的运行结果
虽然异常出现了,sleep被唤醒但是循环没有终止,t 线程任然在继续工作,并没有停止。Java这样设定是因为,当java收到要中断信息的时候,我们希望它可以自由决定接下来怎么处理。
线程等待
等待一个线程我们使用 join()
public class demo8 { public static void main(String[] args) throws InterruptedException { Thread t = new Thread(() -> { for (int i = 0; i < 5; i++) { System.out.println("线程开始工作"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }); t.start(); System.out.println("线程开始等待"); t.join();//主线程等待 t 线程结束 谁调用join谁是被等的 System.out.println("等待结束"); } }
一旦调用 join 主线程就会发生阻塞,这样 t 线程就可以完成后面的工作,主线程要一直阻塞到 t 线程执行结束才会解除阻塞。
方法 | 说明 |
public void join() | 等待线程结束 |
public void join(long millis) | 等待线程结束,最多等 millis 毫秒 |
public void join(long millis, int nanos) | 等待线程结束,但可以更高精度 最多等 nanos 纳秒 |
线程休眠
方法 | 说明 |
public static void sleep(long millis) throws InterruptedException | 休眠当前线程 millis 毫秒 |
public static void sleep(long millis, int nanos) throws InterruptedException | 休眠当前线程 nanos 纳秒 |
public class demo11 { public static void main(String[] args) throws InterruptedException { System.out.println(System.currentTimeMillis()); Thread.sleep(3 * 1000);//休眠三秒 System.out.println(System.currentTimeMillis()); } }