1、多线程的状态
- 线程在一定条件下,状态会发生变化。线程一共有以下几种状态
- 新建状态(New): 创建一个新的线程对象
- 就绪状态(Runnable): 线程对象创建后,调用该对象的start()方法,改线程就进入到就绪状态。这意味着线程已经准备好执行,等待系统分配处理器资源。
- 运行(Running) :线程获得了处理器资源,开始执行run()方法中的代码。这是线程处于活动状态的阶段。
- 阻塞(Blocked):线程在等待某个条件满足时进入阻塞状态。例如,线程可能在等待I/O操作完成或者等待获取锁。
- 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
- 代码示例1
- 多线程的几种状态 (无阻塞)
java
public class MyThread{
public static void main(String[] args) throws Exception {
Thread thread=new Thread(() -> {
System.out.println("线程进入可运行状态");
try {
Thread.sleep(1000); // 让线程休眠1秒,模拟执行任务
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
// 打印线程初始状态
System.out.println("线程初始状态: " + thread.getState());
// 启动线程
thread.start();
// 打印线程可运行状态
System.out.println("线程可运行状态: " + thread.getState());
// 主线程等待子线程执行完毕
thread.join();
// 打印线程终止状态
System.out.println("线程终止状态: " + thread.getState());
}
}
- 代码示例2
- 线程的几种状态(包含阻塞)
java
public class MyThread {
private static Lock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
private static boolean flag = false;
public static void main(String[] args) throws Exception {
Thread thread1 = new Thread(() -> {
try {
System.out.println("线程1进入可运行状态");
lock.lock();
while (!flag) {
condition.await(); // 等待条件满足
}
System.out.println("线程1继续执行");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
Thread thread2 = new Thread(() -> {
try {
System.out.println("线程2进入可运行状态");
Thread.sleep(3000);
lock.lock();
flag = true;
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
});
// 打印线程初始状态
System.out.println("线程1初始状态: " + thread1.getState());
System.out.println("线程2初始状态: " + thread2.getState());
// 启动线程
thread1.start();
thread2.start();
// 打印线程可运行状态
System.out.println("线程1可运行状态: " + thread1.getState());
System.out.println("线程2可运行状态: " + thread2.getState());
try {
// 主线程等待子线程执行完毕
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 打印线程终止状态
System.out.println("线程1终止状态: " + thread1.getState());
System.out.println("线程2终止状态: " + thread2.getState());
}
}
输出结果:
线程1初始状态: NEW
线程2初始状态: NEW
线程1可运行状态: RUNNABLE
线程2可运行状态: RUNNABLE
线程1进入可运行状态
线程2进入可运行状态
线程1继续执行
线程1终止状态: TERMINATED
线程2终止状态: TERMINATED
--------------------------------------
public class MyThread {
public static void main(String[] args) {
Object lock = new Object();
Thread thread1 = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("线程1等待");
lock.wait();
System.out.println("线程1继续执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread thread2 = new Thread(() -> {
synchronized (lock) {
System.out.println("线程2唤醒线程1");
lock.notify();
}
});
thread1.start();
thread2.start();
}
}
输出结果:
线程1等待
线程2唤醒线程1
线程1继续执行
2、 线程中常用的方法
- Thread类的几个常用的方法:
- start():开始执行线程的方法,java虚拟机会调用线程内的run()方法;
- yield():yield在英语里有放弃的意思,同样,这里的yield()指的是当前线程愿意让出对当前处理器的占用。这里需要注意的是,就算当前线程调用了yield()方法,程序在调度的时候,也还有可能继续运行这个线程的;
- sleep():静态方法,使当前线程睡眠一段时间;
- join():使当前线程等待另一个线程执行完毕之后再继续执行,内部调用的是Object类的wait方法实现的;
- Object类的几个常用的方法:
- wait():当一个线程调用一个对象的 wait() 方法时,它会释放该对象的锁,然后进入等待状态。直到其他线程调用相同对象的 notify() 或 notifyAll() 方法来唤醒它。被唤醒后,线程会尝试重新获取对象的锁,并在成功获取锁之后继续执行。如果在调用 wait() 之前没有持有对象的锁,那么会抛出 IllegalMonitorStateException。
- notify(): 当一个线程调用一个对象的 notify() 方法时,它会随机唤醒在该对象上等待的一个线程(如果有的话)。被唤醒的线程将从等待池中移除,并尝试重新获取对象的锁。如果多个线程在等待,只有一个线程会被唤醒。如果没有线程在等待,那么调用 notify() 没有任何效果。
- notifyAll():与 notify() 类似,但不同之处在于它会唤醒所有在该对象上等待的线程。这些线程都将从等待池中移除,并尝试重新获取对象的锁。如果没有线程在等待,那么调用 notifyAll() 同样没有任何效果。
4.wait(), notify(), 和 notifyAll() 必须在同步块或同步方法中使用,因为它们依赖于对象的内置锁。此外,为了避免死锁和活锁等问题,通常使用更高级的并发工具,如 java.util.concurrent 包中的 Lock 和 Condition。
3、线程中添加返回值
- 使用Callable接口和FutureTask类。Callable接口允许你定义一个任务,该任务在执行完成后可以返回一个结果。FutureTask类是一个实现了Runnable接口的类,它接受一个Callable对象作为参数,并在调用run()方法时执行该任务
示例代码:
java
public class MyThread implements Callable<Integer> {
@Override
public Integer call() throws Exception {
Thread.sleep(1000);
return 2;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
//方法1
ExecutorService executorService= Executors.newSingleThreadExecutor();
MyThread myThread=new MyThread();
Future<Integer> submit = executorService.submit(myThread);
// 注意调用get方法会阻塞当前线程,直到得到结果。
// 所以实际编码中建议使用可以设置超时时间的重载get方法。
System.out.println(submit.get());
//方法2
ExecutorService executor = Executors.newCachedThreadPool();
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
executor.submit(futureTask);
System.out.println(futureTask.get());
//方法3
ExecutorService executorService = Executors.newFixedThreadPool(5);
Callable<String> task = () -> {
// 在这里执行你的任务逻辑
return "Hello from thread: " + Thread.currentThread().getName();
};
Future<String> submit = executorService.submit(task);
String rs = submit.get();
System.out.println(rs);
}
}
3、线程间通信
- Java多线程的等待/通知机制是基于Object类的wait()方法和notify(), notifyAll()方法来实现的
java
public class MyThread{
private static volatile int signal = 0;
static class ThreadA implements Runnable {
@Override
public void run() {
while (signal < 5) {
if (signal % 2 == 0) {
System.out.println("threadA: " + signal);
synchronized (this) {
signal++;
}
}
}
}
}
static class ThreadB implements Runnable {
@Override
public void run() {
while (signal < 5) {
if (signal % 2 == 1) {
System.out.println("threadB: " + signal);
synchronized (this) {
signal = signal + 1;
}
}
}
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(new ThreadA()).start();
Thread.sleep(1000);
new Thread(new ThreadB()).start();
}
}
- 使用volatile关键字
java
public class MyThread{
private static volatile boolean flag = false;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
while (!flag) {
// 等待flag变为true
}
System.out.println("当前flag:"+flag);
});
Thread thread2 = new Thread(() -> {
try {
Thread.sleep(1000); // 模拟一些操作
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true; // 修改flag的值
});
thread1.start();
thread2.start();
}
}
- 使用join
java
public class MyThread{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
try {
System.out.println("我是子线程,我先睡一秒");
Thread.sleep(1000);
System.out.println("我是子线程,我睡完了一秒");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
thread.join();
System.out.println("main结束");
}
}