Java中Thread类的使用和它的属性

目录
  • 创建线程
    • 方法一:继承Thread类
    • 方法二:实现Runnable接口中的run()方法
    • 方法三:利用内部类
    • 方法四:使用lambmd表达式
  • 使用线程的好处
  • Thread类的其他属性和方法
  • 给一个线程起名字
  • 判断一个线程是否存活
  • Thread的其他常见属性
    • 创建线程
    • 线程的中断
    • 线程的等待
    • 获取线程的引用
    • 线程的休眠

在java中可以进行多线程编程,在java标准库中提供了一个Thread类,来表示线程操作。Thread类可以视为java标准库提供的一组解决多线程编程的一组API.

创建好的Thread实例,和操作系统中的线程是一一对应的。操作系统提供了关于线程的API(C语言风格),java在对其进行封装就成Thread类。

创建线程

方法一:继承Thread类

class MyThread extends Thread{
    @Override
    public void run() {
        //此时只是定义处理一个线程类,在系统中总是还没有创建出 新的线程。
        System.out.println("hello thread");
    }
}
public class TestDemo11 {
    public static void main(String[] args) {
        //创建线程
        Thread t = new MyThread();
        //启动线程,在系统中创建出了新的线程
        t.start();
    }
}

线程之间是并发执行的

class MyThread3 extends Thread{
    @Override
    public void run() { //定义一个线程类
        while (true) {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class TestDemo13 {
    public static void main(String[] args) {
        Thread t = new MyThread3();
        t.start();//启动t线程
        while(true){
            System.out.println("hello main");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

我们可以看到1s当执行一个线程中的代码之后 进入阻塞状态,那么下一秒要唤醒那个线程呢?

我们可以看到执行出来的两个线程中打印出来的日志的顺序是不确定的。每一轮,1s之后,到底是唤醒thread线程还是唤醒main线程,这是不确定的。(抢占式执行),对于操作系统来说,从宏观上对线程进行调度的顺序是随机的

此处在说明一下Thread.sleep()方法,sleep()这个方法到了ms级别没有那么精确。当调用这个方法之后,把线程强制处于中阻塞(睡眠状态),但是当阻塞时间结束之后,并不是立即在cup上继续执行该线程,如果Thread.sleep(1000),当通过1s后,阻塞时间结束,但是在1001ms,线程也许不会立即执行。也许操作系统中的cup在忙别的线程。或许该线程在1006ms才执行。

方法二:实现Runnable接口中的run()方法

//Runnable其实就是描述一个任务
//创建一个类,实现Runnable接口,重写Runnable类中的run方法,在run()方法中,描述的是该线程要指向的哪些任务。
class MyThread2 implements Runnable{
    @Override
    public void run() {
        System.out.println("hello thread");
    }
}
public class TestDemo12 {
    public static void main(String[] args) {
        //创建线程,把创建好的Runnable实例传给Thread实例
        Thread t = new Thread(new MyThread2());
        t.start();
    }
}

方法三:利用内部类

方法三其实是方法一个的翻版,就是把上面的两种代码,改成了匿名内部类。

public class TestDemo14 {
    public static void main(String[] args) {
        Thread t1 = new MyThread(){
            @Override
            public void run() {
                System.out.println("hello thread1");
            }
        };
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello thread2");
            }
        });
        t1.start();
        t2.start();
    }
}

方法四:使用lambmd表达式

public class TestDemo15 {
    public static void main(String[] args) {
        //其实就是使用lambad表达式代替Runnable
        Thread t1 = new Thread(()->{
            //()表示无参的run()方法
            System.out.println("hello thread1");
        });

    }
}

使用线程的好处

为了更方便的体现出多线程的好处,在这里我们从0开始自增1,一直自增到10_0000_0000,使用串行方法,和并行方法,并且获取他们执行代码的时间,进行比较。

public class TestDemo16 {
    public static void func1() throws InterruptedException {
        long big = System.currentTimeMillis();
        //串行执行
        Thread t = new Thread(()->{
            long a = 0;
           for(long i = 0;i<10_0000_0000;i++){
               a++;
           }
        });
        t.start();
        t.join();
        long end = System.currentTimeMillis();
        System.out.println("串行消耗时间:" + (end - big) + "ms");
    }
    public static void func2() throws InterruptedException {
        long big = System.currentTimeMillis();
        Thread t1 = new Thread(()->{
            long b = 0;
        for(long i = 0;i< 10_0000_0000 / 2;i++){
            b++;
        }
        });
        t1.start();
        Thread t2 = new Thread(()->{
            long c = 0;
            for(long i = 0;i<10_0000_0000/ 2;i++){
                c++;
            }
        });
        t2.start();
        t1.join();
        t2.join();
        long end = System.currentTimeMillis();
        System.out.println("并行执行消耗时间:" + (end - big) + "ms");
    }
    public static void main(String[] args) throws InterruptedException {
        func1();//串行执行
        func2();//并行执行
    }
}

我们可以很明显的看出串行时间要比并行时间长的多,串行时间几乎是并行时间的2倍。

Thread类的其他属性和方法

Thread的常见构造方法

属性 获取方法
ID getId()
名称 getName()
状态 getState()
优先级 getPriority()
是否后台线程 isDaemon()
线程是否存活 isAlive()
线程是否被中断 isinterrupted()
  • ID是线程唯一的标识,不同的线程之间不会重复
  • 名称是各种调试工具用到的
  • 状态标识当前线程的一种情况
  • 优先级高的线程,理论上更容易被执行到
  • 是否存活简单理解为run()方法是否执行结束

给一个线程起名字

Thread(String name) 这个东西是给thread对象起一个名字,具体起什么名字和线程的执行效率无关,起名字主要依靠于程序员,方便程序员在后期进行调试。

public class TestDemo17 {
    public static void main(String[] args) {
        //给线程器名字
        Thread t1 = new Thread(()->{
            while(true) {
                System.out.println("hello thread1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread1");

        Thread t2 = new Thread(()->{
            while(true) {
                System.out.println("hello thread2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread2");
        t1.start();
        t2.start();
    }
}

那么怎样才能看到,我们定义好的线程名字呢?

注意:当我们要查看线程名字的时候,程序必须要正在执行,否则我们查找不到对应的线程名字。

判断一个线程是否存活

简单的说就是操作系统中我们创建出来的线程是否还存在

Thread t 的生命周期和操作系统中对应的线程的生命周期并不是完全一致的。

我们在定义一个线程类后,在调用t.start()方法之前,操作系统中是没有我们创建出来的线程的。在线程类中的run()方法执行完之后,我们在操作系统中创建出来的线程就被销毁了!但是线程t对象还存在。

  • 总而言之,在调用t.start()方法之前,在执行run()方法之后,此时操作系统中是没有我们创建出来的线程的
  • 在调用t.start()方法之后,在指向run()方法之前,此时操作系统中存在我们创建出来的线程 判断该线程是由是后台线程

如果一个线程是后台线程,那么这个线程就不会进行进程退出

如果一个线程是前台线程,那么这个这个线程就会影响到进程的退出。我们以上的代码在创建线程,那些线程都是前台线程,假如现在有前台线程t1,t2, 现在即使main线程执行结束,但是此时还不可以退出线程,必须要将t1,t2线程执行结束之后,整个线程才会结束。

假如现在有两个线程t1,t2,它们都是后台线程,那么如果现在main线程执行结束,整个进程就执行结束,此时我们会强制停止t1,t2线程。

public class TestDemo18 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
            System.out.println("hello thread");
        });
        t.start();
        System.out.println(t.isDaemon());
    }
}
//因为我们创建的是一个前台线程,所以返回false

Thread的其他常见属性

创建线程

创建线程:定义出一个线程类,然后启动线程t.start(),其中start()方法决定系统是不是真的创建出线程。

线程的中断

中断线程简单的可以理解成为就是让该线程中的run()方法执行结束。还有一个特殊的就是main线程,如果想要中断main线程,那么就需要把main线程执行完。

中断线程方法一:设置一个标志位

public class TestDemo21 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
           while(!Thread.currentThread().isInterrupted()) {
               System.out.println("hello thread");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        //启动t线程
        t.start();
        //在main线程中中断t线程
        //5s之后中断t线程
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.interrupt();
    }
}

运行结果:当t线程中的sout语句被执行5次之后,线程停止。

上面的这种写法不够严谨,只适用于该场合,如果化作是别的代码场合的话,有可能不会终止线程。

这里用一种较好的方法,使用Thread类中自带的检查线程是否断开。

Thread.interrputed() 这是一个静态方法 Thread.currentThread().isinterrupted() 其中Thread.cerrentThread()可以获得线程的引用。

t.interrupted()用于中断线程

public class TestDemo21 {
    public static void main(String[] args) {
        Thread t = new Thread(()->{
           while(!Thread.currentThread().isInterrupted()) {
               System.out.println("hello thread");
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        //启动t线程
        t.start();
        //在main线程中中断t线程
        //5s之后中断t线程
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.interrupt();
    }
}

在我们上边中断线程,判断标志位的时候,我们使用的是第三种方法。设置标志位的时候使用的是第一种方法。

在我们的日常开发中,经常会用到Thread.currentThread().isInterrupted()来判断标志位,判断该线程是否被中断。

还有一种方法判断标志位,但是它是一个静态方法,只能判断一个类中只有一个线程的情况下,这个方法就是Thread.isinterrupted()方法

Thread.currentThread().isinterrupted()这个方法判断的是Thread的普通成员,每个实例都有一个标志位。

在我们以后就无脑使用Thread.currentThread().isInterrupted()方法,判断线程是否中断(标志位)

线程的等待

在前面我们也介绍到join()方法,这个方法就是让线程与线程之间,有了一定的执行顺序。我们知道在多线程中的调度是随机的,不确定的,多线程的执行靠调度器安排,该调度器的安排是随机的,不规律的。其实线程等待就是一个行之有效的方法,实际上就是控制线程执行的先后顺序。

public class TestDemo22 {<!--{C}%3C!%2D%2D%20%2D%2D%3E--> public static void main(String[] args) throws InterruptedException {<!--{C}%3C!%2D%2D%20%2D%2D%3E--> Thread t = new Thread(()->{<!--{C}%3C!%2D%2D%20%2D%2D%3E--> for(int i = 0;i<5;i++){<!--{C}%3C!%2D%2D%20%2D%2D%3E--> System.out.println("hello thread"); try {<!--{C}%3C!%2D%2D%20%2D%2D%3E--> Thread.sleep(1000); } catch (InterruptedException e) {<!--{C}%3C!%2D%2D%20%2D%2D%3E--> e.printStackTrace(); } } }); t.start(); t.join();//main线程调用t.join()方法,main线程就处于阻塞状态,当t线程执行完后,唤醒main线程执行后序代码 System.out.println("hello main"); }}

获取线程的引用

使用方法Thread.currentTread()就可以该线程的实例。

public class TestDemo23 {
    public static void main(String[] args) {
         Thread t = new Thread(){
             @Override
             public void run() {
                 //获取当前线程的引用
                 //System.out.println(Thread.currentThread().getName());
                 //因为当前使用的匿名内部类是继承自Thread类,Thread就是该匿名内部类的父类,所以可以通过this得到当前Thread的实例
                 System.out.println(this.getName());
             }
         };
         t.start();
    }
}

线程的休眠

其实线程休眠就是调用Thread.sleep()方法。

回顾之前的学习内容,我们知道我们使用PCB描述一个进程,使用双向链表来组织进程。这种说法是针对一个进程中只有一个线程的情况下。

那么如果一个进程中有多个线程存在,那么每个线程就有一个PCB,那么每个进程都会有一组PCB,

PCB上有一个字段为tgroupId,这个id就相当于是进程的id,进程中的每个线程的tgroupId都是相同的。

那么进程控制块(process contral block)和线程有什么关系呢?

其实在linux中是不区分进程和线程的,所谓的线程是程序员自己搞出来的,实际上linux只认进程控制块(PCB),实际上线程就相当于一个轻量级进程。

到此这篇关于Java中Thread类的使用和它的属性的文章就介绍到这了,更多相关Java Thread类使用和属性内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java多线程编程之使用thread类创建线程

    在Java中创建线程有两种方法:使用Thread类和使用Runnable接口.在使用Runnable接口时需要建立一个Thread实例.因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例.Thread类的构造方法被重载了八次,构造方法如下: 复制代码 代码如下: public Thread( );public Thread(Runnable target);public Thread(String name);public Thread(Ru

  • Java中Thread类的使用和它的属性

    目录 创建线程 方法一:继承Thread类 方法二:实现Runnable接口中的run()方法 方法三:利用内部类 方法四:使用lambmd表达式 使用线程的好处 Thread类的其他属性和方法 给一个线程起名字 判断一个线程是否存活 Thread的其他常见属性 创建线程 线程的中断 线程的等待 获取线程的引用 线程的休眠 在java中可以进行多线程编程,在java标准库中提供了一个Thread类,来表示线程操作.Thread类可以视为java标准库提供的一组解决多线程编程的一组API. 创建好

  • Java中Thread类详解及常用的方法

    目录 一.Thread 的常见构造方法 二.Thread 的常见属性 三.创建线程 四.中断线程 五.线程等待 六.获取线程引用 七.线程休眠 八.线程状态 总结 一.Thread 的常见构造方法 方法 说明 Thread() 创建线程对象 Thread(Runnable target) 使用 Runnable 对象创建线程对象 Thread(String name) 创建线程对象并命名 Thread(Runnable target,String name) 使用 Runnable 对象创建线程

  • Java多线程Thread类的使用及注意事项

    目录 Thread类的基本用法 线程指标 run和start的区别 中断线程 线程等待 进程状态 线程安全问题 synchronized用法 1.直接修饰普通的方法 2.修饰一个代码块 3.修饰一个静态方法 监视器锁monitor lock 死锁的其他场景 volatile Thread类的基本用法 1.创建子类,继承自Thread并且重写run方法: class MyThread extends Thread { @Override public void run() { System.out

  • Java线程编程中Thread类的基础学习教程

    一.线程的状态 在正式学习Thread类中的具体方法之前,我们先来了解一下线程有哪些状态,这个将会有助于后面对Thread类中的方法的理解. 线程从创建到最终的消亡,要经历若干个状态.一般来说,线程包括以下这几个状态:创建(new).就绪(runnable).运行(running).阻塞(blocked).time waiting.waiting.消亡(dead). 当需要新起一个线程来执行某个子任务时,就创建了一个线程.但是线程创建之后,不会立即进入就绪状态,因为线程的运行需要一些条件(比如内

  • Java中获取类路径classpath的简单方法(推荐)

    如下所示: <SPAN style="FONT-SIZE: 18px"> System.out.println("++++++++++++++++++++++++"); String path = System.getProperty("java.class.path"); String path2 = FreeMarkerWriter.class.getProtectionDomain().getCodeSource().getLo

  • 详解Java中Thread 和Runnable区别

    Thread 和Runnable 关系 Thread类是接口Runnable的一个实现类. public class Thread implements Runnable 源码分析 Thread Threa类运行的时候调用start()方法,源代码如下: 调用start()方法,实际运行的是start0方法,方法声明如下: private native void start0() native表明这个方法是个原生函数,即这个函数是用C/C++实现的,被编译成DLL,由Java调用. native

  • Java中Thread.join()的使用方法

    概要 本文分三个部分对Thread.join()进行分析: 1. join() 的示例和作用 2. join() 源码分析 3. 对网上其他分析 join() 的文章提出疑问 1. join() 的示例和作用 1.1 示例 // 父线程 public class Parent { public static void main(String[] args) { // 创建child对象,此时child表示的线程处于NEW状态 Child child = new Child(); // child

  • Java中Thread和Runnable创建线程的方式对比

    目录 一.通过继承Thread创建线程 二.实现Runnable创建检查 三.比较两种创建方式 3.1.多继承 3.2. 数据共享 3.3.线程池 四.源码分析 一.通过继承Thread创建线程 通过继承Thread类,创建一个线程,在主线程中,调用start,让线程处于runnable状态,让系统去运行线程的方法. public class MyThread extends Thread { @Override public void run() { System.out.println("执

  • 详谈java中File类getPath()、getAbsolutePath()、getCanonical的区别

    简单看一下描述,例子最重要. 1.getPath(): 返回定义时的路径,(就是你写什么路径,他就返回什么路径) 2.getAbsolutePath(): 返回绝对路径,但不会处理"."和".."的情况 3.getCanonicalPath(): 返回的是规范化的绝对路径,相当于将getAbsolutePath()中的"."和".."解析成对应的正确的路径 第一个例子:(使用:".\\src\\test.txt&qu

  • 浅谈java中math类中三种取整函数的区别

    math类中三大取整函数 1.ceil 2.floor 3.round 其实三种取整函数挺简单的.只要记住三个函数名翻译过来的汉语便能轻松理解三大函数,下面一一介绍 1.ceil,意思是天花板,java中叫做向上取整,大于等于该数字的最接近的整数 例: math.ceil(13.2)=14 math.ceil(-13.2)=-13 2.floor,意思是地板,java中叫做向下取整,小于等于该数字的最接近的整数 例: math.floor(13.2)=13 math.floor(-13.2)=-

随机推荐