Thread
一、构造方法
1、ThreadGroup group
所属线程组,默认值是Thread.currentThread().getParentGroup(),这句代码是在创建线程的时候执行,所以默认的parentGroup是父线程的线程组
2、Runnable target
这个不用多介绍,其实是策略模式的一种实现,将线程的具体执行单元抽取出来,以策略的方式执行。如果不传,就必须重写Thread.run()方法
3、String name
线程名称,如果不传,默认值是Thread-0、Thread-1、Thread-2 … Thread-n。线程名称不能重复,否则会抛异常
4、long stackSize
线程的栈内存大小,每个平台都不一样,在某些平台中,这个参数可能不管用。默认值是???
二、API
-
setDeamon(boolean) 设置守护线程。true守护线程,parent线程结束后,自动结束子线程,false非守护线程,parent线程结束后,不自动结束子线程。默认非守护线程
-
join 让parent线程等待我执行完毕,在我执行完毕之前,父线程将一直等待我。也可以使用join(mills)指定等待时间,如果超时,父线程会被自动唤醒
-
sleep 让当前线程休眠一段时间,到时间自动唤醒,休眠期间当前线程不会放弃任何锁,也不会放弃CPU的执行权
-
interrupt Object.wait, Thread.sleep, Thread.join会让阻塞线程执行,这时使用interrupt会打断线程的阻塞,线程会收到InterruptedException异常
-
interrupted 判断当前线程是否被中断,静态方法
-
isInterrupted 判断当前线程是否被中断,成员方法
-
isAlive Tests if this thread is alive. A thread is alive if it has been started and has not yet died.
-
Thread.holdsLock(obj) 静态方法 判断线程是否持有obj锁
-
setUncaughtExceptionHandler 设置线程异常监听器
-
enumerate 将当前线程组和所有子线程组中存活的线程查找出来,并存放在入参中
-
yield 暂时放弃CPU执行权,然后所有其他线程根据优先级争取CPU执行权
-
Thread.activeAccount() 静态方法 评估所在线程组和所有子线程组存活的线程数量,是一个不准确的数量,此方法是用来debug或者监控系统用的
三、线程状态
| 状态 | 描述 | 时机 |
|---|---|---|
| NEW | 刚刚创建出来,还没有start | new Thread() |
| RUNNABLE | 已经start,可执行状态。可能在执行,也可能在等待CPU资源调度 | thread.start() |
| BLOCKED | 等待MONITOR,进入或重新进入synchronized method/block | MONITOR.wait() -> MONITOR.notify() |
| WAITING | 等待MONITOR.notify,或者等待被join的线程结束 | MONITOR.wait()、Thread.join() |
| TIMED_WAITING | 等待timeout结束 | MONITOR.wait(timeout)、Thread.sleep(timeout)、thread.join(timeout) |
| TERMINATED | 线程执行结束 | 执行完毕(正常退出和异常退出) |
四、锁
1、synchronized
同步方法和静态同步方法的MONITOR都是谁?
public class SynchronizedTest {
/**
* 这个案例能证明:
* 非静态同步方法的MONITOR是当前对象
* 静态同步方法的MONITOR是当前Class
*/
public static void main(String[] args) {
NonStaticSync obj = new NonStaticSync();
// 这两个线程不能同时打印, 说明其中一个在等待另一个释放锁, 所以两个锁一定是相同的
// 证明非静态同步方法的MONITOR是当前对象
new Thread(obj::test1).start();
new Thread(obj::test2).start();
// 这两个线程不能同时打印, 说明其中一个在等待另一个释放锁, 所以两个锁一定是相同的
// 证明静态同步方法的MONITOR是当前Class
new Thread(StaticSync::test1).start();
new Thread(StaticSync::test2).start();
}
static class NonStaticSync {
public synchronized void test1() {
System.out.println("NonStaticSync.test1");
try {
Thread.sleep(5_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static final Object MONITOR = new Object();
public void test2() {
synchronized (this) {
// synchronized (MONITOR) {
System.out.println("NonStaticSync.test2");
try {
Thread.sleep(5_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class StaticSync {
public static synchronized void test1() {
System.out.println("StaticSync.test1");
try {
Thread.sleep(5_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void test2() {
synchronized (StaticSync.class) {
System.out.println("StaticSync.test2");
try {
Thread.sleep(5_000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
2、死锁是怎么发生的,怎么排查死锁,怎么避免死锁
同步代码多层级调用,避免同步代码多层级调用,就可以避免死锁
jstack pid可以查看死锁
jconsole 也可以检查死锁
static synchronized method
五、如何停止线程
1、优雅的停止线程
public class StopThreadGraceful {
public static void main(String[] args) throws InterruptedException {
Worker worker = new Worker();
worker.start();
Thread.sleep(5_000);
worker.interrupt();
}
private static class Worker extends Thread {
@Override
public void run() {
while (true) {
if (isInterrupted()) {
break;
}
doWork();
}
// 释放资源等操作
}
// @Override
// public void run() {
// while (true) {
// doWork();
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// break;
// }
// }
// // 释放资源等操作
// }
private void doWork() {
System.out.println("working...");
}
}
}
2、强制停止线程
public class StopThreadForce {
/**
* 强制停止线程的思路是: 守护线程
*/
public static void main(String[] args) throws InterruptedException {
ThreadService threadService = ThreadService.newInstance();
threadService.submit(() -> {
while (true) {
System.out.println("------");
}
});
Thread.sleep(5_000);
threadService.shutdown();
}
public static class ThreadService {
private Thread hostThread;
private ThreadService() {}
public static ThreadService newInstance() {
return new ThreadService();
}
public void submit(Runnable runnable) {
hostThread = new Thread(() -> {
Thread targetThread = new Thread(runnable);
targetThread.setDaemon(true);
targetThread.start();
try {
targetThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
hostThread.start();
}
public void shutdown() {
hostThread.interrupt();
}
}
}
六、线程间通信
两种方式:共享内存 or 消息传递
1、共享内存
2、消息传递 Object.notify() & Object.wait()
七、Thread.sleep和Object.wait的区别是什么
1、sleep时Thread的方法,而wait是Object的方法
2、sleep不会放弃CPU执行权,但是wait会放弃CPU执行权
3、sleep不需要MONITOR,wait必须在同步代码块中执行,MONITOR.wait()
4、sleep不需要唤醒,wait则需要唤醒
八、手写线程池
public class SimpleThreadPool {
private static final int DEFAULT_SIZE = 10;
private static final String THREAD_NAME_PREFIX = "simple-thread-pool";
private static final String THREAD_GROUP_NAME = THREAD_NAME_PREFIX;
private static int seq = 0;
private LinkedList<Thread> threads;
private final LinkedList<Runnable> tasks = new LinkedList<>();;
private int size;
private ThreadGroup group;
public SimpleThreadPool() {
this(DEFAULT_SIZE);
}
public SimpleThreadPool(int size) {
this.size = size;
if (size <= 0) {
this.size = DEFAULT_SIZE;
}
init();
}
public void submit(Runnable task) {
synchronized (tasks) {
tasks.addFirst(task);
tasks.notifyAll();
}
}
private void init() {
group = new ThreadGroup(THREAD_GROUP_NAME);
threads = new LinkedList<>();
for (int i = 0; i < size; i++) {
Thread thread = createThread();
threads.addFirst(thread);
}
}
private Thread createThread() {
Thread thread = new Thread(group, () -> {
while (true) {
Runnable runnable;
synchronized (tasks) {
while (tasks.isEmpty()) {
try {
tasks.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
runnable = tasks.removeLast();
}
if (runnable != null) {
runnable.run();
}
}
}, THREAD_NAME_PREFIX + "-" + ++seq);
thread.start();
return thread;
}
public static void main(String[] args) {
SimpleThreadPool pool = new SimpleThreadPool(3);
Random random = new Random();
while (true) {
int count = random.nextInt(20);
System.out.println("count = " + count);
IntStream.range(0, count).forEach(i -> {
pool.submit(() -> {
System.out.println(Thread.currentThread());
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
});
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}