线程基础知识
线程与进程的区别
并发是如何实现的? 快速连续发生的上下文切换(每秒数十次或数百次)来实现的
常用命令
查看进程线程的方法
JAVA中的线程
JAVA中创建线程的方式
- new Thread类或者Thread的继承类
- 实现Runnable接口配置Thread使用:把线程和任务分开 Thread---线程;Runnable--任务
- 使用有返回值&&可以抛出异常常的Callable
package com.test.threadpool;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class ThreadDemo extends Thread {
private String name;
public ThreadDemo(String name) {
this.name = name;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
public static void main(String[] args) {
//----------------------方式1---------------------------
new ThreadDemo("start thread").start();
// 测试 start 与 run的区别
// new ThreadDemo("run thread").run();
//-----------------------方式2--------------------------
// 任务,
Runnable runnable = () -> System.out.println();
// 线程
Thread thread = new Thread(runnable);
thread.start();
// 无返回值 && 无法抛出异常
//----------------------方式3---------------------------
Callable<Integer> callable = () -> new Random().nextInt();
Future future = new FutureTask(callable);
try {
future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
// 有返回值 && 可以抛出异常
//----------------------方式4---------------------------
// lambda的使用方式只是(且仅是)Runnable接口的一种实现方式
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
}
}
class CallableTask implements Callable<Integer> {
@Override
public Integer call() {
return new Random().nextInt();
}
}
Future +Callable 与线程池 + Callable有啥区别?底层应该是一样的
本质上创建线程的方式只有一种,上述所列的方式只是执行任务的不同方式而已。根本原因如下:
Java线程属于内核级线程
JDK1.2——基于操作系统原生线程模型来实现。Sun JDK,它的Windows版本和Linux版本
都使用一对一的线程模型实现,一条Java线程就映射到一条轻量级进程之中。
内核级线程(Kernel Level Thread ,KLT):它们是依赖于内核的,即无论是用户进程中的线
程,还是系统进程中的线程,它们的创建、撤消、切换都由内核实现。
用户级线程(User Level Thread,ULT):操作系统内核不知道应用线程的存在。
因此:java Thread ---> jvm thread ---> os thread,最终通过start()创建线程
上下文切换只能在内核模式下发生
- Thread start() 和 run() 有啥区别?
start()创建线程,run()并没有创建线程(Thread.currentThread().getId() 查看即可)。
- JAVA线程状态(生命周期)
Java 语言中线程共有六种状态,分别是: 1. NEW(初始化状态) 2. RUNNABLE(可运行状态+运行状态) 3. BLOCKED(阻塞状态) 4. WAITING(无时限等待) 5. TIMED_WAITING(有时限等待) 6. TERMINATED(终止状态)
在操作系统层面,Java 线程中的 BLOCKED、WAITING、TIMED_WAITING 是一种状态,
即前面我们提到的休眠状态。也就是说只要 Java 线程处于这三种状态之一,那么这个线程就永
远没有 CPU 的使用权。
BLCKED 只针对synchronized
直观查看线程状态,放开注释后查看未释放锁情况
package com.test.jucdemo.threadbase;
import java.util.concurrent.locks.LockSupport;
import lombok.extern.slf4j.Slf4j;
/**
* @author
*/
@Slf4j
public class ThreadStateTest {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(
// new Runnable() {
// @Override
// public void run() {
// LockSupport.park();
// }
// }
);
log.debug("线程状态:{}", thread.getState());
thread.start();
log.debug("线程状态:{}", thread.getState());
Thread.sleep(100);
log.debug("线程状态:{}", thread.getState());
}
}
阻塞有几种状态
lock.waite()会释放锁
- Thread常用方法
sleep() 不释放执行权
其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出
InterruptedException,并且会清除中断标志(具体代码见interrupt () 演示)
yield()
yield会释放CPU资源,让当前线程从 Running 进入 Runnable状态,让优先级更高
(至少是相同)的线程获得执行机会,不会释放对象锁;
假设当前进程只有main线程,当调用yield之后,main线程会继续运行,因为没有比
它优先级更高的线程;具体的实现依赖于操作系统的任务调度器
join()
等待调用join方法的线程结束之后,程序再继续执行,一般用于等待异步线程执行完结果之
后才能继续运行的场景,保证线程执行的顺序。
public class ThreadJoinDemo {
public static void main(String[] sure) throws InterruptedException {
Thread t = new Thread(() -> {
System.out.println("t begin");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("t finished");
});
long start = System.currentTimeMillis();
t.start();
//主线程等待线程t执行完成
t.join();
System.out.println("执行时间:" + (System.currentTimeMillis() - start));
System.out.println("Main finished");
}
}
stop() 不推荐使用,直接终止线程,存在未执行完成的风险
package com.test.jucdemo.threadbase;
/**
* @author
*/
public class ThreadStopDemo {
private static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "获取锁");
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "执行完成");
});
thread.start();
Thread.sleep(2000);
// 停止thread,并释放锁
thread.stop();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "等待获取锁");
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "获取锁");
}
}).start();
}
}
interrupt() 详情见 优雅的中断线程
- 优雅的中断线程
由于stop()过于简单暴力,实现 优雅的中断线程需要使用 interrupt() 。inetrrupt相关api有:
thread.interrupt() 添加中断标志位,并不会直接中断线程;
Thread.interrupt() 判断是否已经添加了中断标志位,并清除中断标志位
Thread.currentThread().isInterrupted() 判断是否已经添加了中断标志位,但不清除中断标志位
具体代码实现如下:
public class ThreadInterruptTest {
static int i = 0;
public static void main(String[] args) {
System.out.println("begin");
Thread t1 = new Thread(() -> {
while (true) {
i++;
System.out.println(i);
try {
System.out.println(Thread.currentThread().getState());
Thread.sleep(6000);
System.out.println(Thread.currentThread().getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
//Thread.interrupted() 清除中断标志位
//Thread.currentThread().isInterrupted() 不会清除中断标志位
if (Thread.currentThread().isInterrupted()) {
System.out.println("=========");
}
if(i==10){
break;
}
}
});
t1.start();
//不会停止线程t1,只会设置一个中断标志位 flag=true
t1.interrupt();
}
}
Thread.sleep(1); sleep期间也可以感受到中断,同时会清除中断标志位
public class ThreadStopDemo2 implements Runnable {
@Override
public void run() {
int count = 0;
while (!Thread.currentThread().isInterrupted() && count < 1000) {
System.out.println("count = " + count++);
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
//重新设置线程中断状态为true
Thread.currentThread().interrupt();
}
}
System.out.println("线程停止: stop thread");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new ThreadStopDemo2());
thread.start();
Thread.sleep(5);
thread.interrupt();
}
}
- JAVA中线程通信的几种方式?
volatile 和 2种等待唤醒机制
volatile:略
2种等待唤醒机制
Object 的 wait() notify() notifyAll()
public class WaitDemo {
private static Object lock = new Object();
private static boolean flag = true;
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock){
while (flag){
try {
System.out.println("wait start .......");
//等待
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("wait end ....... ");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
if (flag){
synchronized (lock){
if (flag){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//通知
lock.notifyAll();
System.out.println("notify .......");
flag = false;
}
}
}
}
}).start();
}
}
LockSupport LockSupport是JDK中用来实现线程阻塞和唤醒的工具,线程调用park则等待“许可”,调用unpark则为指定线程提供“许可”。使用它可以在任何场合使线程阻塞,可以指定任何线程进行唤醒,并且不用担心阻塞和唤醒操作的顺序,但要注意连续多次唤醒的效果和一次唤醒是一样的。
public class LockSupportTest {
public static void main(String[] args) {
Thread parkThread = new Thread(new ParkThread());
parkThread.start();
System.out.println("唤醒parkThread");
//为指定线程parkThread提供“许可”
LockSupport.unpark(parkThread);
}
static class ParkThread implements Runnable{
@Override
public void run() {
System.out.println("ParkThread开始执行");
// 等待“许可”
LockSupport.park();
System.out.println("ParkThread执行完成");
}
}
}
juc工具类CountLatch与这个有啥区区别了?
参考链接:
https://baijiahao.baidu.com/s?id=1717274005783084258&wfr=spider&for=pc