在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线程,这是不确定的。(抢占式执行),对于操作系统来说,从宏观上对线程进行调度的顺序是随机的
方法二:实现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()方法之前,此时操作系统中存在我们创建出来的线程 判断该线程是由是后台线程
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(); } }
线程的等待
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(); } }
线程的休眠
以上就是Java中Thread类怎么使用和它的属性是什么的详细内容,更多请关注Work网其它相关文章!