草庐IT

【JavaEE】线程的创建及常见方法解析(Tread类)

爱吃大白菜 2023-07-03 原文

目录

1.Tread类介绍

2线程的构造方法——创建线程

1.继承Thread类,重写run()方法

2.使用Runnbable接口创建线程

3.继承 Thread, 重写 run, 使用匿名内部类

4.实现 Runnable, 重写 run, 使用匿名内部类

5.使用 lambda 表达式(重点掌握)

3.Tread类常见方法解读 

3.1Tread类常见构造方法 

3.2 Tread类的几个常见属性

3.3启动一个线程-start()方法

3.4中断一个线程

3.5等待一个线程-join() 

3.6休眠线程

3.7 实现一个简单的多线程

4.线程的状态

4.1线程的六种状态 

4.2线程状态和状态转移


1.Tread类介绍

Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联,每个执行流(线程),也需要有一个对象来描述, Thread 类的对象就是用来描述一个线程执行流的,JVM 会将这些 Thread 对象组织起来,用于线程调度、线程管理。

2线程的构造方法——创建线程

无论使用哪一个方法创建线程,我们都需要将其中的run方法重写(run方法中写入的是线程需要执行的任务),它的作用就相当于主线程的main方法。

1.继承Thread类,重写run()方法

这个构造方法没有参数,通过创造一个类继承Thread类来实现线程 ,线程必须重写run()方法才算完成。当我们new一个线程时,此时的线程还没开始执行,再用创建的类调用start()方法才算运行了这个线程。

class TestTread extends Thread{
    @Override
    public void run() {
        System.out.println("this tread1");
    }
}
public class testDemo2 {
    public static void main(String[] args) {
        Thread thread=new TestTread();
        thread.start();
    }
    
}

2.使用Runnbable接口创建线程

我们可以看出,该构造方法的参数类型是一个接口类,因此我们需要创建一个类来实现这个接口,再new一个实现Runnable的类对象,再new一个线程,将之前创建的对象放入到参数这里。

class TestTread2 implements Runnable {
    @Override
    public void run() {
        System.out.println("this t2");
    }
}
public class testDemo2 {
    public static void main(String[] args) {
        //new一个实现Runnable接口的对象
        TestTread2 testTread2=new TestTread2();
        //传入参数完成线程的创建
        Thread t2=new Thread(testTread2);
        t2.start();
    }

}

3.继承 Thread, 重写 run, 使用匿名内部类

 使用匿名内部类可以省略创建对象的过程,可以减少资源消耗。

public class testDemo3 {
    public static void main(String[] args) {
        //通过Thread匿名内部类的方法创建一个线程
        Thread t1=new Thread() {
            @Override
            public void run() {
                System.out.println("this t1");
            }
        };
        t1.start();
    }

}

4.实现 Runnable, 重写 run, 使用匿名内部类

这个和3一样,可以少创建一个对象,减少资源的消耗。 

public class testDemo4 {
    public static void main(String[] args) {
        Thread t1=new Thread(new  Runnable() {
            @Override
            public void run() {
                System.out.println("this t1");
            }
        });
        t1.start();
    }
}

5.使用 lambda 表达式(重点掌握)

它的实现原理和匿名内部类相似,这点不了解lambda表达式的,可以去看一下如何使用。

public class testDemo4 {
    public static void main(String[] args) 
        Thread t2=new Thread(()->{
            System.out.println("this t2");
        });
        t2.start();
    }
}

3.Tread类常见方法解读 

3.1Tread类常见构造方法 

方法说明
Thread()创建线程对象
Thread(Runnable target)使用 Runnable 对象创建线程对象
Thread(String name)创建线程对象,并命名
Thread(Runnable target, String name)使用 Runnable 对象创建线程对象,并命名
【了解】Thread(ThreadGroup group,
Runnable target)
线程可以被用来分组管理,分好的组即为线程组,这
个目前我们了解即可

3.2 Tread类的几个常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
  • ID 是线程的唯一标识,不同线程不会重复。
  • 名称是各种调试工具用到。
  • 状态表示线程当前所处的一个情况,下面我们会进一步说明。
  • 优先级高的线程理论上来说更容易被调度到。
  • 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行
  • 是否存活,即简单的理解,为 run 方法是否运行结束了。
  • 线程的中断问题,下方会有解析

3.3启动一个线程-start()方法

调用 start 方法, 才算是在操作系统的底层创建出一个线程. 

public class testDemo4 {
    public static void main(String[] args) {
        Thread t1=new Thread(new  Runnable() {
            @Override
            public void run() {
                System.out.println("this t1");
            }
        });
        t1.start();
    }
}

3.4中断一个线程

方法说明
public void interrupt()中断对象关联的线程,如果线程正在阻塞,则以异常方式通知,
否则设置标志位
public static boolean
interrupted()
判断当前线程的中断标志位是否设置,调用后清除标志位
public boolean
isInterrupted()
判断对象关联的线程的标志位是否设置,调用后不清除标志位
  • 使用 thread 对象的 interrupted() 方法通知线程结束 
public class testDemo6 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(() ->{
            while (!Thread.interrupted()) {
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    break;
                }
            }
        });
        t1.start();
        Thread.sleep(1000*2);
        t1.interrupt();
    }
}

t1收到通知的方式有两种:

1.如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通知,清除中断标志.

  • 当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择忽略这个异常, 也可以跳出循环结束线程(加break).

 2.否则,只是内部的一个中断标志被设置,thread 可以通过

  • Thread.interrupted() 判断当前线程的中断标志被设置,清除中断标志
  • Thread.currentThread().isInterrupted() 判断指定线程的中断标志被设置,不清除中断标志
  • 使用 Thread.isInterrupted() , 线程中断会清除标志位

标志位是否清除, 就类似于一个开关.
Thread.Interrupted() 相当于按下开关, 开关自动弹起来了. 这个称为 "清除标志位".

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    // //打印标准位
                    System.out.println(Thread.interrupted());
                }
        });
        thread.start();
        thread.interrupt();
    }
}

结果:

观察结果可以看出,已知interrupted()初始是true,之后就打印的值就是false,因为标志位已经被删除了。

  • 使用 Thread.currentThread().isInterrupted() , 线程中断标记位不会清除

 标志位是否清除, 就类似于一个开关.
Thread.currentThread().isInterrupted() 相当于按下开关之后, 开关弹不起来, 这个称为"不清除标志位".

public class ThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(()->{
                for (int i = 0; i < 10; i++) {
                    // //打印标准位
                    System.out.println(Thread.currentThread().isInterrupted());
                }
        });
        thread.start();
        thread.interrupt();
    }
}

结果: 

观察结果可以清晰的看出,打印的全是true,这是因为标志位没有被删除,它的值还是true。

3.5等待一个线程-join() 

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos)同理,但可以更高精度
public class testDemo7 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1=new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println("第"+i+"次打印"+Thread.currentThread().getName());
            }
            System.out.println("----------");
        });
        Thread t2=new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println("第"+i+"次打印"+Thread.currentThread().getName());
            }
        });
        t1.start();
        //t2等待t1执行完毕,t2才可执行
        t1.join();
        t2.start();
    }
}

 结果:

 可以看出使用了join(),两个线程不再是杂乱运行了,而是先运行完t1线程,再运行的t2线程。这就是join 的作用。

3.6休眠线程

这个是比较熟悉一组方法,有一点要记得,因为线程的调度是不可控的,所以,这个方法只能保证实际休眠时间是大于等于参数设置的休眠时间的。

方法说明
public static void sleep(long millis) throws InterruptedException休眠当前线程 millis
毫秒
public static void sleep(long millis, int nanos) throws
InterruptedException
可以更高精度的休眠

3.7 实现一个简单的多线程

 这两个线程任务都是打印自己的名字,其中使用的currentThread()方法是或者当前线程的引用,getName()方法就是或者线程的名字(就算我们没有给线程起名字,系统也会给它自定义一个名字)。

public class testDemo5 {
    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            while (true) {
                //打印线程名称
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread t2=new Thread(()->{
            while (true){
                //打印线程名称
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //执行线程任务
        t1.start();
        t2.start();
    }
}

代码实现结果:

观察可以看出,两个线程是交叉运行,并且是杂乱运行,好不规律可言。没错,线程的并发就是没有规律的,谁先运行取决于操作系统如何调度线程,因此线程是"抢占式执行"。

4.线程的状态

4.1线程的六种状态 

  • NEW: 安排了工作, 还未开始行动,刚刚创建一个Tread对象,还没开始工作。
  • RUNNABLE: 可工作的. 又可以分成正在工作中和即将开始工作(正在CPU上执行任务或者在就绪队列中随时可以在CPU上执行的)。
  • BLOCKED: 这几个都表示排队等着其他事情(synchronized加锁)。
  • WAITING: 这几个都表示排队等着其他事情。
  • TIMED_WAITING: 这几个都表示排队等着其他事情(使用sleep或者join方法引起的)。
  • TERMINATED: 工作完成了。

4.2线程状态和状态转移

  举个例子,看下图:

有关【JavaEE】线程的创建及常见方法解析(Tread类)的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. Ruby 解析字符串 - 2

    我有一个字符串input="maybe(thisis|thatwas)some((nice|ugly)(day|night)|(strange(weather|time)))"Ruby中解析该字符串的最佳方法是什么?我的意思是脚本应该能够像这样构建句子:maybethisissomeuglynightmaybethatwassomenicenightmaybethiswassomestrangetime等等,你明白了......我应该一个字符一个字符地读取字符串并构建一个带有堆栈的状态机来存储括号值以供以后计算,还是有更好的方法?也许为此目的准备了一个开箱即用的库?

  4. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  5. ruby - 如何在 Ruby 中顺序创建 PI - 2

    出于纯粹的兴趣,我很好奇如何按顺序创建PI,而不是在过程结果之后生成数字,而是让数字在过程本身生成时显示。如果是这种情况,那么数字可以自行产生,我可以对以前看到的数字实现垃圾收集,从而创建一个无限系列。结果只是在Pi系列之后每秒生成一个数字。这是我通过互联网筛选的结果:这是流行的计算机友好算法,类机器算法:defarccot(x,unity)xpow=unity/xn=1sign=1sum=0loopdoterm=xpow/nbreakifterm==0sum+=sign*(xpow/n)xpow/=x*xn+=2sign=-signendsumenddefcalc_pi(digits

  6. ruby - Facter::Util::Uptime:Module 的未定义方法 get_uptime (NoMethodError) - 2

    我正在尝试设置一个puppet节点,但ruby​​gems似乎不正常。如果我通过它自己的二进制文件(/usr/lib/ruby/gems/1.8/gems/facter-1.5.8/bin/facter)在cli上运行facter,它工作正常,但如果我通过由ruby​​gems(/usr/bin/facter)安装的二进制文件,它抛出:/usr/lib/ruby/1.8/facter/uptime.rb:11:undefinedmethod`get_uptime'forFacter::Util::Uptime:Module(NoMethodError)from/usr/lib/ruby

  7. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  8. Ruby 方法() 方法 - 2

    我想了解Ruby方法methods()是如何工作的。我尝试使用“ruby方法”在Google上搜索,但这不是我需要的。我也看过ruby​​-doc.org,但我没有找到这种方法。你能详细解释一下它是如何工作的或者给我一个链接吗?更新我用methods()方法做了实验,得到了这样的结果:'labrat'代码classFirstdeffirst_instance_mymethodenddefself.first_class_mymethodendendclassSecond使用类#returnsavailablemethodslistforclassandancestorsputsSeco

  9. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  10. ruby - 使用 Vim Rails,您可以创建一个新的迁移文件并一次性打开它吗? - 2

    使用带有Rails插件的vim,您可以创建一个迁移文件,然后一次性打开该文件吗?textmate也可以这样吗? 最佳答案 你可以使用rails.vim然后做类似的事情::Rgeneratemigratonadd_foo_to_bar插件将打开迁移生成的文件,这正是您想要的。我不能代表textmate。 关于ruby-使用VimRails,您可以创建一个新的迁移文件并一次性打开它吗?,我们在StackOverflow上找到一个类似的问题: https://sta

随机推荐