线程

Thread 本身实现了 Runnable 接口。

Thread 和 Runnable:

  1. //Runnable 测试
  2. val runnable =object :Runnable{
  3. override fun run() {
  4. println("111")
  5. }
  6. }
  7. val thread1 = Thread(runnable)
  8. thread1.start()
  9. //Thread 测试
  10. val thread2 = object : Thread() {
  11. override fun run() {
  12. super.run()
  13. println("222")
  14. }
  15. }
  16. thread2.start()
  17. --------------------------------------------------------------------------------------------------------
  18. 输出结果:
  19. 111
  20. 222

Thread 实现了 Runnable 接口。 下面看一下 Thread 实现的 run 方法:

  1. //Thread.java
  2. @Override
  3. public void run() {
  4. if (target != null) {
  5. target.run();
  6. }
  7. }

这个 target 就是我们通过构造方法传入的自定义实现的 Runnable 对象。

ThreadFactory:

  1. val threadFactory = object : ThreadFactory {
  2. @Volatile
  3. var count: Int = 0
  4. override fun newThread(r: Runnable): Thread {
  5. count++
  6. return Thread(r, "$count")
  7. }
  8. }
  9. val runnable2 = object :Runnable{
  10. override fun run() {
  11. println(Thread.currentThread().name)
  12. }
  13. }
  14. threadFactory.newThread(runnable2).start()
  15. threadFactory.newThread(runnable2).start()
  16. threadFactory.newThread(runnable2).start()
  17. threadFactory.newThread(runnable2).start()
  18. --------------------------------------------------------------------------------------------------------
  19. 输出结果:
  20. 1
  21. 2
  22. 3
  23. 4

Thread 常用的几个方法

  • currentThread():静态方法,返回对当前正在执行的线程对象的引用;
  • start():开始执行线程的方法,java虚拟机会调用线程内的run()方法;
  • yield():yield 在英语里有放弃的意思,同样,这里的yield()指的是当前线程愿意让出对当前处理器的占用。这里需要注意的是,就算当前线程调用了yield()方法,程序在调度的时候,也还有可能继续运行这个线程的;
  • sleep():静态方法,使当前线程睡眠一段时间;
  • join():使当前线程等待另一个线程执行完毕之后再继续执行,内部调用的是Object类的wait方法实现的;

join() 代码示例

  1. val thread1 = object : Thread() {
  2. override fun run() {
  3. super.run()
  4. for (i in 0..5) {
  5. println("thread1 线程--数值:$i")
  6. }
  7. }
  8. }
  9. thread1.start()
  10. thread1.join()
  11. for (i in 0..5) {
  12. println("main 线程--数值:$i")
  13. }
  14. --------------------------------------------------------------------------------------------------------
  15. 输出结果:
  16. thread1 线程--数值:0
  17. thread1 线程--数值:1
  18. thread1 线程--数值:2
  19. thread1 线程--数值:3
  20. thread1 线程--数值:4
  21. thread1 线程--数值:5
  22. main 线程--数值:0
  23. main 线程--数值:1
  24. main 线程--数值:2
  25. main 线程--数值:3
  26. main 线程--数值:4
  27. main 线程--数值:5

如果这里不加 join,Thread1 和 main 线程可能交替打印数值,加了以后,main 线程就会等待 Thread1 执行完后,再进行执行。

线程结束后,可以再次 start 吗?

不可以。

  1. //Thread.java
  2. public synchronized void start() {
  3. if (started)
  4. throw new IllegalThreadStateException();
  5. group.add(this);
  6. started = false;
  7. try {
  8. nativeCreate(this, stackSize, daemon);
  9. started = true;
  10. } finally {
  11. try {
  12. if (!started) {
  13. group.threadStartFailed(this);
  14. }
  15. } catch (Throwable ignore) {
  16. }
  17. }
  18. }

通过以上代码可以看出,started 为 true 的时候,会抛出异常。而在进行一次 start 后,就会把 started 置为 true。所以不能一个线程不能 start 两次。

Executor 和线程池

常用:newCachedThreadPool

创建无限多线程的线程池

  1. val executors1 = Executors.newCachedThreadPool()
  2. val runnable = object : Runnable {
  3. override fun run() {
  4. println(Thread.currentThread().name)
  5. }
  6. }
  7. executors1.execute (runnable)
  8. executors1.execute (runnable)
  9. executors1.execute (runnable)
  10. executors1.execute (runnable)
  11. --------------------------------------------------------------------------------------------------------
  12. 输出结果:
  13. pool-1-thread-1
  14. pool-1-thread-3
  15. pool-1-thread-2
  16. pool-1-thread-4

短时批量处理:newFixedThreadPool

创建固定大小的线程池。

  1. val runnable = object : Runnable {
  2. override fun run() {
  3. println(Thread.currentThread().name)
  4. }
  5. }
  6. val executors2 = Executors.newFixedThreadPool(4)
  7. executors2.execute(runnable)
  8. executors2.execute(runnable)
  9. executors2.execute(runnable)
  10. executors2.execute(runnable)
  11. --------------------------------------------------------------------------------------------------------
  12. 输出结果:
  13. pool-2-thread-1
  14. pool-2-thread-3
  15. pool-2-thread-4
  16. pool-2-thread-2

Callable 和 Future

  1. val callable = object : Callable<String> {
  2. override fun call(): String {
  3. try {
  4. Thread.sleep(5000)
  5. } finally {
  6. }
  7. return "张三"
  8. }
  9. }
  10. val executors3: ExecutorService = Executors.newCachedThreadPool()
  11. val submit: Future<String> = executors3.submit(callable)
  12. try {
  13. val value: String = submit.get()
  14. println(value)
  15. } catch (e: Exception) {
  16. e.printStackTrace()
  17. }
  18. --------------------------------------------------------------------------------------------------------
  19. 输出结果:(等待了 5 秒钟输出结果)
  20. 张三

注意调用get方法会阻塞当前线程,直到得到结果。所以实际编码中建议使用可以设置超时时间的重载get方法。

下面是 Future 接口

  1. public abstract interface Future<V> {
  2. public abstract boolean cancel(boolean paramBoolean);
  3. public abstract boolean isCancelled();
  4. public abstract boolean isDone();
  5. public abstract V get() throws InterruptedException, ExecutionException;
  6. public abstract V get(long paramLong, TimeUnit paramTimeUnit)
  7. throws InterruptedException, ExecutionException, TimeoutException;
  8. }

cancel方法是试图取消一个线程的执行。

注意是试图取消,并不一定能取消成功。因为任务可能已完成、已取消、或者一些其它因素不能取消,存在取消失败的可能。boolean类型的返回值是“是否取消成功”的意思。参数paramBoolean表示是否采用中断的方式取消线程执行。

所以有时候,为了让任务有能够取消的功能,就使用Callable来代替Runnable。如果为了可取消性而使用 Future但又不提供可用的结果,则可以声明 Future<?>形式类型、并返回 null作为底层任务的结果。

FutureTask

Future接口有一个实现类叫FutureTaskFutureTask是实现的RunnableFuture接口的,而RunnableFuture接口同时继承了Runnable接口和Future接口:

  1. //RunnableFuture.java
  2. public interface RunnableFuture<V> extends Runnable, Future<V> {
  3. void run();
  4. }

FutureTask类有什么用?为什么要有一个FutureTask类?前面说到了Future只是一个接口,而它里面的cancelgetisDone等方法要自己实现起来都是非常复杂的。所以JDK提供了一个FutureTask类来供我们使用。

示例:

  1. val callable = object : Callable<String> {
  2. override fun call(): String {
  3. try {
  4. Thread.sleep(5000)
  5. } finally {
  6. }
  7. return "张三"
  8. }
  9. }
  10. val executors4: ExecutorService = Executors.newCachedThreadPool()
  11. val futureTask: FutureTask<String> = FutureTask(callable)
  12. executors4.submit(futureTask)
  13. try {
  14. val value: String = futureTask.get()
  15. println(value)
  16. } catch (e: Exception) {
  17. e.printStackTrace()
  18. }
  19. --------------------------------------------------------------------------------------------------------
  20. 输出结果:(等待了 5 秒钟输出结果)
  21. 张三

Future 有所不同的是,Future 是从 submit 的返回值 Future 中获取返回值。FutureTask 是直接从 FutureTask 中获取返回值。

线程组和线程优先级

Java 中用 ThreadGroup 来表示线程组,我们可以使用线程组对线程进行批量控制。

每一个 Thread 必然存在于一个 ThreadGroup 中,Thread 不能独立于 ThreadGroup 存在。

线程组:ThreadGroup,可以统一限制线程组内线程优先级的最大值。创建一个线程组和线程的时候,不指定线程组,默认指定当前执行创建操作线程所在的线程组为自己的线程组。

  1. fun main() {
  2. val thread = object : Thread() {
  3. override fun run() {
  4. super.run()
  5. println("当前线程的名称 111:${Thread.currentThread()}, hashCode 为:${Thread.currentThread().hashCode()}")
  6. println("当前线程的所在线程组的名称 111:${Thread.currentThread().threadGroup}, hashCode 为:${Thread.currentThread().threadGroup?.hashCode()}")
  7. }
  8. }
  9. thread.start()
  10. println("当前线程的名称 222:${Thread.currentThread()}, hashCode 为:${Thread.currentThread().hashCode()}")
  11. println("当前线程的所在线程组的名称 222:${Thread.currentThread().threadGroup}, hashCode 为:${Thread.currentThread().threadGroup?.hashCode()}")
  12. }
  13. --------------------------------------------------------------------------------------------------------
  14. 输出结果:
  15. 当前线程的名称 111Thread[Thread-0,5,main], hashCode 为:2017079673
  16. 当前线程的名称 222Thread[main,5,main], hashCode 为:2016447921
  17. 当前线程的所在线程组的名称 111java.lang.ThreadGroup[name=main,maxpri=10], hashCode 为:666988784
  18. 当前线程的所在线程组的名称 222java.lang.ThreadGroup[name=main,maxpri=10], hashCode 为:666988784

线程组中有一个方法来控制线程组中线程的最大优先级 setMaxPriority(int pri) 。最大优先级默认为 10。

线程优先级

  1. 线程的优先级设定范围为 1-10。
  2. 优先级越大,首先执行的概率越高。只是越高而已,具体还要看操作系统的调度算法。
  3. 线程的默认优先级为 5。
  4. 线程的优先级默认不能超过所在的线程组的最大优先级。

守护线程

  1. 当前所有非守护线程结束后,守护线程也会结束。
  2. 通过Thread类的setDaemon(boolean on) 来设置是否为守护线程。

示例代码:

  1. val thread1 = object : Thread() {
  2. override fun run() {
  3. super.run()
  4. Thread.sleep(1000)
  5. println("111111")
  6. }
  7. }
  8. val thread2 = object : Thread() {
  9. override fun run() {
  10. super.run()
  11. Thread.sleep(2000)
  12. println("222222")
  13. }
  14. }
  15. val thread3 = object : Thread() {
  16. override fun run() {
  17. super.run()
  18. Thread.sleep(3000)
  19. println("333333")
  20. }
  21. }
  22. thread3.isDaemon = true
  23. thread1.start()
  24. thread2.start()
  25. thread3.start()
  26. --------------------------------------------------------------------------------------------------------
  27. 输出结果:
  28. 111111
  29. 222222

这里等待了 3 秒后, thread3 没有输出 “333333”。

参考文章

Java多线程入门类和接口
线程组和线程优先级
Java线程唤醒与阻塞是什么?看完就明白啦!