哈哈,上边扯了好大一下子犊子才绕到java语法这😅。没办法呀,你不了解故事背景直接看剧情老是会有些懵逼的~java语言中的线程是对操做系统线程的一种抽象,有些地方可能不太一致,遇到了再和你们说哈~java
咱们以前说过,main
方法是程序入口,咱们对已经编译好的class文件
调用java
命令时就能够运行一个java程序。这个过程当中,其实系统自动为咱们建立了一个进程和一个线程,并且这个线程的名字就叫作main
。main线程
是用来执行咱们的程序的,不过系统还会为咱们建立一些辅助线程
来帮助main线程
的执行,如今就先不说它们是啥了,等遇到了再说哈~编程
除了系统本身建立的这个main线程
之外,咱们还能够本身在程序里建立一些线程。不过咱们前边说过,线程实际上是去执行任务
的,因此咱们先看怎么定义任务
。ide
java中的任务被抽象成了一个Runnable接口
:字体
public interface Runnable { public void run(); }
咱们的自定义任务
须要去实现这个接口,并把任务
的详细内容写在覆盖的run
方法里,好比咱们定义一个输出一个字符串的任务:操作系统
public class PrintTask implements Runnable { @Override public void run() { System.out.println("输出一行字"); } }
看到了吧,定义一个任务
就是这么简单哈~不过光有任务没啥卵用,须要建立一个线程去运行这个任务
线程
java中的Thread
类来表明一个线程,咱们须要关注它的这几种构造方法:设计
Thread(Runnable target, String name)
在建立线程对象的时候传入须要执行的任务
以及这个线程的名称。code
Thread(Runnable target)
Thread(String name)
Thread()
执行任务对象
Thread
类的start()
方法负责开始执行一个线程
,让一个线程运行起来有这么两种方法:继承
任务
:public class Test { public static void main(String[] args) { new Thread(new PrintTask()).start(); } }
执行结果是:
输出一行字
Thread
类并覆盖run
方法:Thread
类自己就表明了一个Runnable
任务,咱们看Thread类的定义:
public class Thread implements Runnable { private Runnable target; @Override public void run() { if (target != null) { target.run(); } } // ... 为省略篇幅,省略其余方法和字段 }
其中的target
就是在构造方法里传入的,若是构造方法不传这个字段的话,很显然run
方法就是一个空实现,因此若是咱们想运行这个线程,就继承它而且覆盖一下run
方法吧:
public class PrintThread extends Thread { @Override public void run() { System.out.println("输出一行字"); } }
由于PrintThread中已经有一个任务了,因此直接调用start方法运行它就好:
public class Test { public static void main(String[] args) { new PrintThread().start(); } }
执行结果是:
输出一行字
这两种执行任务的方法说不上谁好谁坏,可是使用继承Thread
类而且覆盖run
方法的方式把线程和任务给弄到了一起了,不可分割了,也就是所谓的耦合了,因此咱们平时更倾向于使用任务和线程分开处理的第1种执行任务的方式。固然,有时候为了演示的方便,也是会使用继承Thread
类而且覆盖run
方法的方式~
Thread类提供了许多方法来方便咱们获取线程的信息或者控制线程,下边来一下都有哪些重要的方法吧:
获取线程ID
System.out.println(new Thread().getId()); System.out.println(new Thread().getId()); System.out.println(new Thread().getId());
执行结果:
10 11 12
获取和设置线程名称
固然,咱们也能够经过构造方法去设置Thread的名称:
Thread t1 = new Thread("t1"); Thread t2 = new Thread(); t2.setName("t2"); System.out.println("t1线程的名称是:" + t1.getName()); System.out.println("t2线程的名称是:" + t2.getName());
执行结果是:
t1线程的名称是:t1 t2线程的名称是:t2
设置线程的优先级
咱们知道处理器会从就绪的队列里挑一个已经就绪的线程去执行,每一个线程均可以有不一样的优先级,优先级越高,越容易被处理器选中执行。
java中的优先级是用一个正数来表示,共有1~10个等级,其中,设计java的大叔们用了是三个静态变量表示咱们经常使用的:
通常状况下,咱们就用这三个变量去表示优先级就够用了。
下面看个例子:
Thread t1 = new Thread("t1"); System.out.Println("t1线程的优先级是:" + t1.getpriority()); t1.setPriority(Thread.MAX_PRIORITY); System.out.Println("t1线程的优先级是:" + t1.getpriority());
注意:线程优先级并不意味着得不处处理器执行,而只是执行的频次低一点而已。并且线程的优先级通常不用咱们主动去设置,因此这两个方法对咱们来讲基本没啥用~
休眠
若是想在线程执行过程当中让程序停一段时间以后再执行,这个中止一段时间也叫作休眠,就好像睡一段时间而后醒来。能够经过sleep()方法来实现休眠:
程序在指定的毫秒数加纳秒数内让当前正在执行的线程休眠,也就是暂停执行。
程序在指定的毫秒数加纳秒数内让当前正在执行的线程休眠,也就是暂停执行。
你们注意到这个sleep方法是一个静态方法,它会让当前线程暂停指定的时间。这个所谓的暂停,或者说休眠其实只是把正在运行的线程阻塞掉,放到阻塞队列里,等指定的时间一到,再从阻塞的队列里出来而已。另外,这个方法有InterruptedException的异常,说明咱们在调用的时候须要catch一下:
public static void main(String[] args) { System.out.println(1); try { Thread.sleep(1000L); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(2); try { Thread.sleep(1000L); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.print(3); }
你们在执行的过程当中会发现,每隔一秒会输出一个数字。另外,因为是在main方法中调用的sleep方法,因此其实休眠的是main线程,你们能够试试在本身的线程里休眠哈~
让出本次处理器的时间片
咱们知道线程是处理器时间片的分配单位。不一样的线程排着对等着处理器赏赐给一个时间片用来执行一下子代码。若是一个线程已经得到了一个时间片正在执行,它忽然不想执行了,能够放弃这次的时间片时间,先让处理器给别的线程分配一个时间片,而他参与下一轮的时间分配。
举个例子:好比说咱们把处理器比做皇帝,把各个线程比做妃子,各个妃子争相让皇帝宠幸,为了公平,皇帝只能一次宠幸一个妃子五分钟。皇帝怎么挑选妃子有他本身的小算盘,可是,每一个五分钟都会选一个去陪她。此时有一个妃子被皇帝挑选中了,但是她配了皇帝五分钟有些尿急,因此她主动说先放弃此次宠幸,先出去放放水,而后再排队被挑选吧。因此只陪了皇帝两分钟的她便退出了这次宠幸,放水回来后再加入到覅诶这大军中等待皇帝挑选。
这个yield方法只是建议处理器不要在这次时间片时间内继续执行本线程,最后实际怎么着还不必定呢,另外,yield是一个静态方法,表示让出当前线程本次时间片的时间。
也就是说你想放弃就放弃是不可能的,这还得看皇帝的心情~