正文
一、线程名称设置和获取
1、线程名称一般在线程启动前设置,但也允许为正在运行的线程设置名称。在实际开发中,在使用多线程开发时,一定要自定义线程名称,偏于查找日志。
2、线程允许使用同样的名字,但应该尽量避免。
3、如果线程没有命名,系统会自动为线程设置名称。例如Thread-0,Thread-1。在SpringBoot中http-nio-端口号-exec-1,http-nio-端口号-exec-2。
线程命名
package com.xiaojie.juc.thread.base; /** * @author xiaojie * @version 1.0 * @description: 线程命名和获取名称 * @date 2021/12/17 22:54 */ public class ThreadNameDemo { public static void main(String[] args) { A a = new A(); new Thread(a,"mythraed-a").start(); new Thread(a,"mythraed-b").start(); } static class A implements Runnable{ @Override public void run() { //获取线程名称 System.out.println("当前线程的名称是>>>>>>>>:"+Thread.currentThread().getName()); } } }
线程池命名
package com.xiaojie.juc.thread.base; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; /** * @author xiaojie * @version 1.0 * @description: 线程池命名 * @date 2021/12/17 23:09 */ public class ThreadPoolNameDemo { private static final AtomicInteger threadNumber = new AtomicInteger(1); private static final String MYTHREAD_POOL_GROUP = "mythread-pool-group-thread-"; public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3, r -> new Thread(new ThreadGroup(""), r, MYTHREAD_POOL_GROUP + threadNumber.getAndIncrement(), 0)); for (int i = 0; i < 10; i++) { executorService.execute(() -> System.out.println("当前线程名称>>>>>>>>>:" + Thread.currentThread().getName())); } //关闭线程池 executorService.shutdown(); } }
二、线程的sleep()
package com.xiaojie.juc.thread.base; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @author xiaojie * @version 1.0 * @description: 线程sleep方法 * @date 2021/12/17 23:46 */ public class ThreadSleepDemo { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(3); for (int i = 0; i < 10; i++) { executorService.execute(() -> { try { if (Thread.currentThread().isInterrupted()) { System.out.println("Thread is Interrupted"); } //一定要捕捉InterruptedException异常 Thread.sleep(1000); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName()); }); } //关闭线程池 executorService.shutdown(); } }
sleep的作用是让目前正在执行的线程休眠,让CPU去执行其他任务,使线程从运行状态进去阻塞状态。当线程睡眠时间满后,线程并不一定是立刻马上开始执行,因为CPU可能正在执行其他任务,线程进入就绪状态,等待CPU分配时间片后才能执行。
三、线程的interrupt
Thread提供了stop(),但是是个过时方法,不推荐使用。stop()方法是强制中断线程,不管当前线程是否正在运行,有可能导致当前线程持有当前锁,强行中断后可能不会释放锁。interrupt()并不是用来中断一个线程,而是将线程设置为中断状态。
1、如果执行interrupt()时,当前线程处于阻塞状态(wait(),sleep(),join())时,线程会立刻退出阻塞,抛出InterruptedException异常。
2、如果调用interrupt()时,线程处于运行状态,线程不受任何影响,只是会将中断标识设置为true,可以调用isInterrupted(),判断是否中断。
package com.xiaojie.juc.thread.base; /** * @author xiaojie * @version 1.0 * @description: 线程中断 * @date 2021/12/18 0:12 */ public class InterruptDemo { public static void main(String[] args) { A a = new A(); a.start(); } static class A extends Thread { @Override public void run() { //手动中断线程 Thread.currentThread().interrupt(); try { while (true) { if (Thread.currentThread().isInterrupted()) { System.out.println("线程中断了"); break; } } Thread.sleep(3000); System.out.println(Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("线程中断了。。。。。when sleep"); } } } }
四、join()
线程A在运行时,线程Bjoin(),之后,线程A会等待直到线程B运行完之后,才开始执行。如果线程B无限制运行,线程A可以设置等待时长join(1000)单位是毫秒。
package com.xiaojie.juc.thread.base; /** * @author xiaojie * @version 1.0 * @description:线程join模拟 * @date 2021/12/18 0:45 */ public class ThreadJoinDemo { public static void main(String[] args) { new A().start(); } static class A extends Thread { @Override public void run() { try { B b = new B(); b.start(); b.join();//B线程join之后,A线程需要等待B运行完之后才执行 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程A执行任务。。。。。。。"); } } static class B extends Thread { @Override public void run() { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程B执行任务。。。。。。。"); } } }
五、yield()
yield的作用是让正在执行的线程让出CPU的执行权限,使得CPU可以去执行其他任务。从线程状态来看,线程从运行变为就绪状态。线程在yield()方法之后,线程放弃和重占CPU是不确定的,可能是刚刚放弃CPU,马上又获得CPU执行权。yield不会阻塞线程,只会让当前线程暂停一下,让系统线程调度器重新调度一次。
package com.xiaojie.juc.thread.base; /** * @author xiaojie * @version 1.0 * @description: Yield实例 * @date 2021/12/18 1:13 */ public class ThreadYieldDemo { public static void main(String[] args) { Thread threadA = new Thread(new A()); threadA.setPriority(1); threadA.setName("threadA "); Thread threadB = new Thread(new B()); threadB.setName("threadB "); threadA.start(); threadB.start(); } static class A implements Runnable { @Override public void run() { //暂停一下,重新调度线程 Thread.yield(); System.out.println(Thread.currentThread().getName()); } } static class B implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } } }
六、wait(),notify(),notifyAll()
这三个方法不是Thread独有的方法,是Object的方法!!!
当一个对象实例调用了wait()方法后,当前线程会在这个对象上等待。直到其他线程调用这个实例的notify(),或者notifyAll()方法后才会唤醒。调用了wait()方法后,该线程就会进入该实例的等待队列,如果队列中有多个线程,notify()只会随机的唤醒一个线程。这个选择是不公平的,不是先等待的就会先唤醒。notifyAll(),是唤醒所有的等待线程。而且这些方法只能在synchronized代码块中使用。wait()和sleep()都会让线程等待,但是wait()方法会释放目标对象的锁,而sleep()方法不会释放任何资源。
package com.xiaojie.juc.thread.base; /** * @author xiaojie * @version 1.0 * @description: wait demo * @date 2021/12/18 1:43 */ public class ThreadWaitDemo { private static final Object obj = new Object(); public static void main(String[] args) { new Thread(new A()).start(); new Thread(new B()).start(); } static class A implements Runnable { @Override public void run() { synchronized (obj) { System.out.println("线程A开始等待。。。。。。"); try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程A等待结束。。。。。。"); System.out.println("线程A开始执行。。。。。。"); } } } static class B implements Runnable { @Override public void run() { synchronized (obj) { try { obj.notify(); Thread.sleep(2000); System.out.println("线程B运行。。。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } } } }
参考:《JAVA高并发核心编程(卷2):多线程、锁、JMM、JUC、高并发设计》-尼恩编著