Rumah >Java >javaTutorial >Cara menggunakan kelas Thread di Java dan apakah atributnya
Pengaturcaraan berbilang benang boleh dilakukan dalam java, dan kelas Thread disediakan dalam perpustakaan standard java untuk mewakili operasi benang. Kelas Thread boleh dianggap sebagai satu set API yang disediakan oleh pustaka standard Java untuk menyelesaikan pengaturcaraan berbilang thread
Contoh Thread yang dibuat mempunyai surat-menyurat satu dengan satu dengan utas dalam sistem pengendalian. Sistem pengendalian menyediakan API (gaya bahasa C) tentang benang, dan Java merangkumnya ke dalam kelas 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(); } }
Thread dilaksanakan serentak
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(); } } } }
Kita dapat lihat bahawa 1s当执行一个线程中的代码之后
memasuki keadaan menyekat, jadi benang apakah yang perlu dibangkitkan detik seterusnya?
Kita dapat melihat bahawa susunan log yang dicetak dalam dua utas yang dilaksanakan adalah tidak pasti. Dalam setiap pusingan, selepas 1s, adalah tidak pasti sama ada untuk membangunkan benang benang atau benang utama. (Preemptive execution), untuk sistem pengendalian, urutan urutan yang dijadualkan adalah secara makroskopik rawak
Di sini kami akan menerangkan kaedah Thread.sleep() dan kaedah sleep(). tepat pada tahap ms. Apabila kaedah ini dipanggil, benang terpaksa berada dalam sekatan sederhana (keadaan tidur), tetapi apabila masa sekatan tamat, benang tidak akan terus dilaksanakan pada cawan dengan serta-merta. Jika Thread.sleep(1000), selepas itu 1s berlalu, masa menyekat akan Tamat, tetapi pada 1001ms, urutan mungkin tidak dilaksanakan serta-merta. Mungkin cawan dalam sistem operasi sibuk dengan benang lain. Mungkin benang dilaksanakan pada 1006ms.
//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(); } }
Kaedah 3 sebenarnya adalah satu kaedah Pembuatan semula adalah untuk menukar dua kod di atas menjadi kelas dalaman tanpa nama.
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(); } }
public class TestDemo15 { public static void main(String[] args) { //其实就是使用lambad表达式代替Runnable Thread t1 = new Thread(()->{ //()表示无参的run()方法 System.out.println("hello thread1"); }); } }
Untuk lebih mudah mencerminkan pelbagai faedah benang ialah di sini kita bermula dari 0 dan meningkat sebanyak 1 sehingga mencapai 10_0000_0000 Kami menggunakan kaedah bersiri dan kaedah selari, dan mendapatkan masa mereka melaksanakan kod untuk perbandingan.
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();//并行执行 } }
Kita dapat melihat dengan jelas bahawa masa bersiri adalah lebih lama daripada masa selari, dan masa bersiri hampir dua kali lebih lama daripada masa selari.
Kaedah pembinaan biasa 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
创建线程:定义出一个线程类,然后启动线程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),实际上线程就相当于一个轻量级进程。
Atas ialah kandungan terperinci Cara menggunakan kelas Thread di Java dan apakah atributnya. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!