Java并发性-了解线程的基础知识

2020-10-31 21:01:35

Java Thread对象允许我们在单独的线程中运行代码。当应用程序启动时,JVM创建名为main的初始线程。Main方法在主线程上运行。在应用程序内部,我们可以创建新线程,以便与主线程并行执行其他任务。

Java使用本机操作系统线程。因此,一个操作系统线程映射一个Java线程。

Thread类的构造函数接受一个Runnable对象。Runnable接口有一个抽象的run方法,该方法由Thread#start()方法调用。IT对象可以由lambda、匿名类或实现Runnable方法的类实例化。

只要线程的Run钩子方法没有返回,线程就会存活。调度程序可以挂起并多次运行线程。为了让线程永远执行,它需要一个无限循环来防止它返回。

Join方法允许一个线程等待另一个线程的完成。这是屏障同步的一种简单形式。

当JVM启动时,它包含名为主线程的单个用户线程。用户线程和守护进程线程之间的主要区别在于它们退出时会发生什么。

线程类包含布尔型守护程序字段,用于指定线程是否为守护程序。它可以在创建时由构造函数或setter方法设置。

默认情况下,守护程序字段为false,因此我们生成的大多数线程都是用户线程。如果未指定,线程将复制父威胁的isDaemon状态。Java在某些地方使用守护程序线程,比如ForkJoinPool和Timer。为了说明这一点,我们可以使用以下示例:

Public class main{public static void main(String[]args)Thws InterruptedException,ExecutionException{//runDeamonThread();runUserThread();System.out.println(getCurrentThreadName()+";exits";);}私有静态void runDeamonThread()抛出ExecutionException,InterruptedException{ExecutorService ExecutorService=Executors.newWorkStealingPool(10);ecutorService.Execue.Exception();}Private static void runrunRserThread();{Thread=new Thread(getthread.start();}private static runnable Runnable();{return()&gT;T;{for(int i=0;i<;=200;i++){System.out.print(";.";);Thread.Year();}System.out.println(getCurrentThreadName()+";退出。IsDeamon:";+isDaemon());};}私有静态布尔isDaemon(){return Thread.currentThread().isDaemon();}私有静态字符串getCurrentThreadName(){return Thread.currentThread().getName();}}。

第二种情况是调用runDeamonThread,它使用ForkJoinPool作为守护程序线程的示例。我可以简单地使用setDaemon(True)方法,但是我想给出一个用法示例。产出:

因此,当main方法退出时,所有用户线程都将终止,JVM将退出并终止所有守护进程线程,因此我们甚至没有机会看到守护进程线程的输出。

与创建相比,停止线程是一件相当困难的事情。一旦线程开始运行,它就脱离了调用方,它就有了自己的生命周期。它可以完成任务并退出,或者如果它执行长时间运行的操作,它可以永远工作。Java没有为我们提供自愿停止线程的方法(非弃用的)。

易失性布尔isStoped=false;public void test(){new Thread(()->;{while(!isStoped){System.out.print(";.";);}System.out.println(";子出口";);}).start();try{Thread.sleep(100);}catch(InterruptedException E){e.printStackTrace();}isStoped=true;System.out.println(";main exits";);

请注意,该标志是易失性的,以便使其最新值对两个线程都可见。但是,如果线程正在执行休眠、等待、联接或阻塞I/O操作等阻塞操作,则此方法会失败。

2.另一种停止踏步的方法是使用线程的interrupt()方法。

对线程的中断请求是它应该停止正在做的事情并做其他事情的指示。线程如何响应中断是由程序员来决定的,但是线程终止是很常见的。

要使中断机制正常工作,被中断的线程必须支持其自己的中断机制。我们可以检查两种情况是否中断:

在这种情况下,调用thread.interrupt()方法将设置该线程的中断标志,但如果任务本身不检查中断标志的状态,则不会产生任何影响。例如:

Public void test()抛出InterruptedException{ThreadThread=new Thread(()->;{System.out.println(";Child Start";);While(True){System.out.print(";.";);}});thread.start();thread.interrupt();thread.join();System.out.println(";main exits";);}。

为了让线程捕获中断,它应该反复检查中断标志的状态,以便了解是否有任何挂起的中断请求,并相应地处理该请求。

因此,我们可以检查WHILE循环中的标志,如果为真,我们可以返回或中断循环。在lambda表达式中,不可能抛出异常,但是在适当的位置,我们也可以抛出InterruptedException。

Public void test()抛出InterruptedException{ThreadThread=new Thread(()->;{System.out.println(";Child Start";);While(True){if(Thread.interrupted(){Break;}System.out.print(";.";);}System.out.println(";Child Exits";);});thread.start();thread.interrupt();thread.join();System.out.println(";主要出口);}。

注Thread.interrupted()方法返回标志的值,如果为真,则将其清除。因此,如果我们希望在堆栈的上层保持Thread的状态为中断,可以使用Thread.currentThread().interrupt()将其设置回去;

如果线程频繁调用所有都以中断方式运行的阻塞方法(如等待、联接、睡眠、阻塞I/O),则这些方法会在内部检查它们是否已被中断,如果已中断,它们将自动抛出InterruptedException。应该在适当的上下文中捕获和处理此异常。以下示例在阻塞休眠操作中使用中断来中断循环:

Public void test()抛出中断异常{ThreadThreadThread=new Thread(()->;{System.out.println(";Child Start";);try{While(True){线程睡眠(10000);}}catch(InterruptedException E){System.out.println(";线程中断:";+e.getMessage());}System.out.println(";Child Exits";);});thread.start();thread.interrupt();Thread.join();System.out.println(";Main Exits";);}。

一种方法是将异常传播给调用方,因此将由较高层负责。

如果不能重新抛出,我们可以使用Thread.currentThread().interrupt()再次将中断状态设置为true,以便在较高层希望检查证据时保留证据。

因此,总而言之,如果我们想要实现可取消的任务,我们需要定期检查中断状态的状态,并以线程将退出的方式处理中断。

为了简化线程管理,可以使用对相关线程进行分组的java.lang.ThreadGroup对象来组织多个线程。每个线程组都需要有一个父组。在层次结构中,有一个主组,它是我们在程序中创建的其他组或线程的父组。我们可以通过使用父组和/或名称调用ThreadGroup的构造函数来创建ThreadGroup。要将Thread添加到组中,我们需要在Thread的构造函数中指定该组。

Public void test(){ThreadGroup TG1=new ThreadGroup(";Thread-group-1";);ThreadGroup TG2=new ThreadGroup(TG1,";Thread-group-2";);Thread thread1=new Thread(TG1,";Thread-1";);Thread Thread2=new Thread(TG2,";Thread-2";);Thread Thread3=new Thread(TG2,";Thread-3";);thread1.start();thread2.start();Thread3.start();Thread[]Thread=new Thread[tg2.activeCount()];tg2.枚举(线程);Arrays.asList(线程).forEach(t->;System.out.println(t.getName();tg1.list();}。

我们可以通过调用枚举方法迭代线程,该方法用组的线程引用填充给定数组。

Public class ThreadPool{//创建线程组字段Private Final ThreadGroup group=new ThreadGroup(";ThreadPoolGroup";);//创建包含可运行私有最终列表<;Runnable>;Tasks=new LinkedList<;>;();public ThreadPool(Int PoolSize){//为(int i=0;i<;poolSize;i++){var worker=new worker(group,";worker-#34;+i);worker.start();在线程组中创建多个工作线程。}}Private Runnable Take()抛出InterruptedException{Synchronized(Tasks){//如果LinkedList为空,我们等待(tasks.isEmpty())tasks.wait();//从LinkedList中移除第一个作业并返回任务。remove(0);}}public void mit(Runnable Job){//将作业添加到LinkedList并通知所有同步(Tasks){tasks.add(Job);tasks.notfyAll();}}public int getRunQueueLength(){//返回LinkedList的长度//记得也要同步!Synchronized(Tasks){return tasks.size();}}public void shutdown(){//这将停止组中的所有线程。interrupt();}私有类worker扩展Thread{public worker(ThreadGroup group,String name){Super(group,name);}public void run(){//我们在无限循环中运行:while(True){//使用take()从链表中删除下一个作业//然后对作业try{take().run();调用run()方法;}catch(InterruptedException E){e.printStackTrace();Break;}

Java ThreadLocal类可用于创建其值只能由同一线程访问的变量。因此,即使两个线程正在执行相同的代码,并且代码引用了相同的ThreadLocal变量,这两个线程也无法看到对方的ThreadLocal变量。

公共类Main{public static class ThreadLocalStorage{private static inal ThreadLocal<;String>;threadLocal=new ThreadLocal<;>;();public static void setName(String Name){threadLocal.set(Name);}public static string getName(){return threadLocal.get();}}public static void main(String[]args){ThreadLocalStorage.setName(";main thread";);runnable runnable=()->;{ThreadLocalStorage.setName(getCurrentThreadName());try{Thread.Slear(1000);}catch(InterruptedException E){e.printStackTrace();}System.out.println(";Thread:[";+getCurrentThreadName()+";]";+";-value:[";+ThreadLocalStorage.getName()+";]";);};Thread thread1=new Thread(Runnable);Thread thread2=new Thread(Runnable);thread1.start();thread2.start();Systemout.println(";main exits";);};Thread thread1=new Thread(Runnable);Thread thread2=new Thread(Runnable);thread1.start();thread2.start();Systemout.println(";main exits";)。);}私有静态字符串getCurrentThreadName(){return Thread.currentThread().getName();}}。

如果我们运行代码,我们可以看到每个线程都有自己的ThreadLocal对象副本。

InheritableThreadLocal不是每个线程在ThreadLocal内都有自己的值,而是将值的访问权限授予一个线程和该线程创建的所有子线程。