线程的创建
java语言本身是没有线程的,只是提供了线程的创建和启动的方法。在线程调用start()方法的时候,JVM会创建一个新的操作系统线程,并将这个线程与Java线程相关联,并且启动这个线程交由操作系统进行调度。当线程获取CPU时间片的时候,M会调用与之关联的Java线程的run()方法。
线程的创建方式
-
集成Thread类,重写run()方法
class MyThread extends Thread { @Override public void run() { System.out.println("继承Thread,重写run方法创建线程"); } } public class Main { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } }
-
实现Runable接口
class MyRunnable implements Runnable { @Override public void run() { System.out.println("实现Runnable接口,重写run方法"); } } public class Main { public static void main(String[] args) { MyRunnable myRunnable = new MyRunnable(); Thread thread = new Thread(myRunnable); thread.start(); } }
-
使用匿名内部类创建Thread子类对象
public class Main { public static void main(String[] args) { Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("使用匿名内部类,实例Runnable接口作为构造参数"); } }); thread.start(); } }
-
lambda表达式
public class Main { public static void main(String[] args) { Thread thread = new Thread(() -> { System.out.println("使用lambda表示创建线程"); }); thread.start(); } }
-
实现Callable接口
class MyCallableTest implements Callable<Integer> { @Override public Integer call() throws Exception { System.out.println("创建线程:" + Thread.currentThread().getName()); return 2; } } public class Main { public static void main(String[] args) throws ExecutionException, InterruptedException { FutureTask<Integer> task = new FutureTask<>(new MyCallableTest()); Thread thread = new Thread(task); thread.start(); System.out.println("创建线程的返回结果为:" + task.get()); } }
-
使用线程池创建线程
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Pool { public static void main(String[] args) { ExecutorService pool = Executors.newCachedThreadPool(); pool.submit(new Runnable() { @Override public void run() { //执行业务逻辑 for(int i = 1; i <= 100; i++) { System.out.println("线程:" + Thread.currentThread().getName() + "执行了任务" + i + "~"); } } }); pool.submit(new Runnable() { @Override public void run() { //执行业务逻辑 for(int i = 101; i <= 200; i++) { System.out.println("线程:" + Thread.currentThread().getName() + "执行了任务" + i + "~"); } } }); pool.submit(new Runnable() { @Override public void run() { //执行业务逻辑 for(int i = 201; i <= 300; i++) { System.out.println("线程:" + Thread.currentThread().getName() + "执行了任务" + i + "~"); } } }); } }
线程的生命周期
//线程的状态
public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED; }
-
新建状态 New
Thread t = new Thread()
-
就绪状态 Runable
//当线程对象调用start()方法后,改线程处于就绪状态,等待备用获取cpu时间片后开始运行 t.start();
-
运行状态 Running
当线程获取cpu时间片后,进入运行状态,开始执行run()方法中的代码
-
等待状态 Waiting
// 通过调用一下的方法,线程会由运行状态进入等待状态,在等待状态下是不被分配cpu时间片,只能通过其他线程唤醒让线程进入就绪状态 t.wait(); t.join(); LockSupport.park();
-
超时等待状态 Timed Waiting
//通一下方法的调用,线程由运行状态进入超时等待状态,当超时时间到达后,线程会自动进入就绪状态 sleep(long ms); wait(long ms); join(long ms);
-
阻塞状态 Blocked
//通过一下方法的调用,会将线程唤醒,进入就绪状态进行竞争锁资源,竞争失败就进入到阻塞状态 notify(); notifyAll(); LocKSupport.unPark();
-
终止状态 Terminatred
当线程的run方法执行完毕,或者线程中断,线程就会由就绪状态进入到终止状态
线程的状态转换
现成的状态由Runable -> Waiting 或者 由 Waiting -> Runable 时,cpu就会进行上下文的切换
线程的常用方法
-
sleep(long)
静态方法、不会释放锁、会让出cpu资源
sleep(0) 等价于yield()方法
让线程进入等待超时状态后指定的毫秒数后会自动唤醒进入就绪状态,或者调用interrupt方法来唤醒此线程public static void main(String[] args) throws InterruptedException { System.out.println("开始睡眠---"+System.currentTimeMillis()); Thread.sleep(10000); System.out.println("结束睡眠---"+System.currentTimeMillis()); }
开始睡眠---1714049045589 结束睡眠---1714049055601
-
wait() 和 wait(long)
实例方法、会释放锁、会让出cpu资源
让线程由运行状态进入等待状态。在不设置超时时间的情况,只能等到其他线程的唤醒后进入就绪状态;若设置超时时间,会在等待指定时间后 或 被其他线程唤醒,自动进入就绪状态
必须得在同步的上下文中由锁对象进行调用,例如同步代码块或同步方法中,因为wait底层是由minitor监视器实现的 -
notify() 和 notifyAll()
实例方法
唤醒等待状态的线程,进入就绪状态
必须得在上同步的上下文中由锁对象进行调用,例如同步代码块或同步方法中,因为这两个方法底层是由minitor监视器实现的
notify只会随机选取一个处于等待池中的线程进入锁池去竞争获取锁的机会
notifyAll会让所有处于等待池的线程全部进入锁池去竞争获取锁的机会public class WorkTest { public static void main(String[] args) throws InterruptedException { Integer a = 2; Thread t1 = new Thread(()->{ while(true){ synchronized (a){ System.out.println("线程1开始等待" + System.currentTimeMillis()); try { a.wait(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程1结束等待" + System.currentTimeMillis()); a.notify(); } } }); Thread t2 = new Thread(()->{ while(true){ synchronized (a){ System.out.println("线程2开始等待" + System.currentTimeMillis()); try { a.wait(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程2结束等待" + System.currentTimeMillis()); a.notify(); } } }); t1.start(); t2.start(); } }
-
yield()
静态方法 不会释放锁 会让出cpu资源
让线程由运行状态进入到就绪状态 -
interrupt()
实例方法
用于将线程的中断状态位设置为 true,单独使用并不会影响线程的执行,需要配合其他方法进行使用
如果线程在执行sleep()、wait()、join()方法时,会抛出 java.lang.InterruptedException 异常
public class WorkTest {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
System.out.println("开始睡眠---"+System.currentTimeMillis());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束睡眠---"+System.currentTimeMillis());
});
t.start();
Thread.sleep(2000);
t.interrupt();
}
}
开始睡眠---1714051673116
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at WorkTest.lambda$main$0(WorkTest.java:20)
at java.lang.Thread.run(Thread.java:748)
结束睡眠---1714051675122
-
interrupted()
静态方法
判断当前线程是否被中断,如果是清楚中断标记,返回true;否则返回fase -
isInterrupted()
只是获取线程的中断状态
-
join()
实例方式 会释放Thread的锁,不会释放对象锁 会让出cpu资源
让当前线程由运行状态进入等待状态,等待目标线程执行完再进入就绪状态继续执行
join(0) 等价于 join()
join()方法的底层是通过 synchronized wait() 和 notifyAll() 来实现的线程通信
如果在A主线程中,一次join了多个其他线程B/C/D,则被join的多个子线程执行的先后顺序不影响,都是并行执行。
如果有两个线程同时调用另外一个线程的join方法,会有一个线程成功得到锁,而另外一个则必须等待,进入阻塞状态,而在得到锁之后,才会执行join方法
public final synchronized void join(long millis)throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}