原文:https://www.jianshu.com/p/13addbedc965
static Thread currentThread()------- 返回对当前正在执行的线程对象的引用。
long getId()------- 返回该线程的标识符。
String getName()------- 返回该线程的名称。
int getPriority() ------- 返回线程的优先级。
void interrupt() ------- 中断线程。
boolean isAlive()------- 测试线程是否处于活动状态。
void join()------- 等待该线程终止。
void join(long millis)------- 等待该线程终止的时间最长为 millis 毫秒。
void join(long millis, int nanos)------- 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。
void setDaemon(boolean on)------- 将该线程标记为守护线程或用户线程。
void setPriority(int newPriority)------- 更改线程的优先级。
static void sleep(long millis)------- 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
static void sleep(long millis, int nanos)------- 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
void start() ------- 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
static void yield()------- 暂停当前正在执行的线程对象,并执行其他线程。
join
当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join()方法加入的join线程执行完为止。
public class Join {
public static void main(String[] args) {
Thread thread = new JoinThread();
thread.start();
try {
//主线程等待thread的业务处理完了之后再向下运行
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i = 0; i < 5; i++){
System.out.println(Thread.currentThread().getName()+" -- " + i);
}
}
}
class JoinThread extends Thread{
@Override
public void run() {
for(int i = 0; i < 5; i++){
System.out.println(Thread.currentThread().getName() + " -- "+i);
try {
sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//---------------运行结果---------------------
//主线程等待JoinThread执行完再执行
Thread-0 -- 0
Thread-0 -- 1
Thread-0 -- 2
Thread-0 -- 3
Thread-0 -- 4
main -- 0
main -- 1
main -- 2
main -- 3
main -- 4
sleep
Thread.sleep(long millis)
和Thread.sleep(long millis, int nanos)
静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。
当线程睡眠时,它睡在某个地方,在苏醒之前不会返回到可运行状态。
当睡眠时间到期,则返回到可运行状态。
线程睡眠的原因:线程执行太快,或者需要强制进入下一轮,因为Java规范不保证合理的轮换。
睡眠的实现:调用静态方法。
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠。
注意:
1、线程睡眠是帮助所有线程获得运行机会的最好方法。
2、线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会 运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。
3、sleep()是静态方法,只能控制当前正在运行的线程。
4、sleep()在同步块中执行,不会释放对象机锁,其他对象无法访问该对象直到sleep()方法执行完并释放机锁。
public class Sleep {
public static void main(String[] args) {
new SleepThred().start();
}
}
class SleepThred extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if ((i) % 2 == 0) {
System.out.println("-------" + i);
}
System.out.print(i);
try {
Thread.sleep(1000);
System.out.print(" 线程睡眠1秒!\n");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//---------------运行结果---------------------
//计数到10,在每个数字之间暂停1秒,每隔2个数字输出一个字符串
-------0
0 线程睡眠1秒!
1 线程睡眠1秒!
-------2
2 线程睡眠1秒!
3 线程睡眠1秒!
-------4
4 线程睡眠1秒!
5 线程睡眠1秒!
-------6
6 线程睡眠1秒!
7 线程睡眠1秒!
-------8
8 线程睡眠1秒!
9 线程睡眠1秒!
sleep和wait的区别
Sleep
1、sleep()使当前线程进入停滞状态(阻塞当前线程),让出CPU的使用权,目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;
2、 sleep()是Thread类的Static(静态)的方法,因此他不能改变对象的机锁,所以当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,但是对象的机锁并木有被释放,其他线程无法访问这个对象(即使睡着也持有对象锁)。
3、 在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。
Wait
1、wait()方法是Object类里的方法,当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还需要返还对象锁),其他线程可以访问。
2、wait()使用notify或者notifyAll或者指定睡眠时间来唤醒当前等待池中的线程。
3、wiat()必须放在synchronized block中,否则会在program runtime时扔出 ”java.lang.IllegalMonitorStateException“异常。
sleep()和wait()方法的最大区别是:
1、sleep()睡眠时,保持对象锁,仍然占有该锁
2、 wait()睡眠时,释放对象锁。
3、wait()和sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptedException(但不建议使用该方法)。
public class MultiThread {
private static class Thread1 implements Runnable{
@Override
public void run() {
//由于 Thread1和下面Thread2内部run方法要用同一对象作为监视器,如果用this则Thread1和Threa2的this不是同一对象
//所以用MultiThread.class这个字节码对象,当前虚拟机里引用这个变量时指向的都是同一个对象
synchronized(MultiThread.class){
System.out.println("enter thread1 ...");
System.out.println("thread1 is waiting");
try{
//释放锁有两种方式:(1)程序自然离开监视器的范围,即离开synchronized关键字管辖的代码范围
//(2)在synchronized关键字管辖的代码内部调用监视器对象的wait()方法。这里使用wait方法
MultiThread.class.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("thread1 is going on ...");
System.out.println("thread1 is being over!");
}
}
}
private static class Thread2 implements Runnable{
@Override
public void run() {
//notify方法并不释放锁,即使thread2调用了下面的sleep方法休息10ms,但thread1仍然不会执行
//因为thread2没有释放锁,所以Thread1得不到锁而无法执行
synchronized(MultiThread.class){
System.out.println("enter thread2 ...");
System.out.println("thread2 notify other thread can release wait status ...");
MultiThread.class.notify();
System.out.println("thread2 is sleeping ten millisecond ...");
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("thread2 is going on ...");
System.out.println("thread2 is being over!");
}
}
}
public static void main(String[] args) {
new Thread(new Thread1()).start();
try{
Thread.sleep(10);
}catch(InterruptedException e){
e.printStackTrace();
}
new Thread(new Thread2()).start();
}
}
yield
yield()方法和sleep()方法有点相似,它也是Thread类提供的一个静态方法,它也可以让当前正在执行的线程暂停,但它不会阻塞该线程,它只是将该线程转入到就绪状态。即让当前线程暂停一下,让系统的线程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield()方法暂停之后,线程调度器又将其调度出来重新执行。
实际上,当某个线程调用了yield()方法之后,只有优先级与当前线程相同或者比当前线程更高的处于就绪状态的线程才会获得执行机会。
sleep()与yield()方法区别
1、sleep()方法暂停当前线程后,会给其他线程执行机会,不会理会其他线程的优先级;但yield()方法只会给优先级高或者相同的线程机会。
2、sleep()方法会将线程转入到阻塞状态,直到经过阻塞时间才会转入就绪状态;而yield()不会将线程转入阻塞状态,它只是强制当前线程进入就绪状态。因此完全有可能某个线程调用了yield()方法暂停之后,立即再次获取处理器资源被执行。
3、sleep()方法声明抛出InterruptedException异常,所以调用sleep()方法时要么捕捉该异常,要么显示声明抛出该异常;而yield()方法则没有声明抛出任何异常。
4、sleep()方法比yield()方法更好的可移植性,通常不建议使用yield()方法来控制并发线程执行。
public class YieldExample
{
public static void main(String[] args)
{
Thread producer = new Producer();
Thread consumer = new Consumer();
producer.setPriority(Thread.MIN_PRIORITY); //Min Priority
consumer.setPriority(Thread.MAX_PRIORITY); //Max Priority
producer.start();
consumer.start();
}
}
class Producer extends Thread
{
public void run()
{
for (int i = 0; i < 5; I++)
{
System.out.println("I am Producer : Produced Item " + i);
Thread.yield();
}
}
}
class Consumer extends Thread
{
public void run()
{
for (int i = 0; i < 5; I++)
{
System.out.println("I am Consumer : Consumed Item " + i);
Thread.yield();
}
}
}
//---------------运行结果---------------------
//调用yield,两个线程交替打印,依次把执行机会交给对方
I am Producer : Produced Item 0
I am Consumer : Consumed Item 0
I am Producer : Produced Item 1
I am Consumer : Consumed Item 1
I am Producer : Produced Item 2
I am Consumer : Consumed Item 2
I am Producer : Produced Item 3
I am Consumer : Consumed Item 3
I am Producer : Produced Item 4
I am Consumer : Consumed Item 4
isAlive
判断线程是否处于活动状态
public class isAlive {
public static void main(String[] args) {
isAliveThread m = new isAliveThread();
Thread t = new Thread(m, "自定义线程");
System.out.println("线程执行前:" + t.isAlive()); //false
t.start();
System.out.println("线程启动之后:" + t.isAlive()); //true
}
}
class isAliveThread implements Runnable
{
public void run() {
System.out.println(Thread.currentThread().getName() + "运行");
}
}
//---------------运行结果---------------------
//线程启动之后run方法体才会被执行
线程执行前:false
线程启动之后:true
自定义线程运行
线程优先级
线程的优先级用数字表示,范围从1到10,默认的是为5
每个线程默认的优先级与创建它的父线程的优先级相同
优先级越高的线程,被执行的顺序就比较靠前,在Thread中存在三个常量:
- MAX_PRIORITY
- MIN_PRIORITY
- NORM_PRIORITY
public class Priority {
public static void main(String[] args) {
Thread t1 = new Thread(new PriorityThread(), "线程A");
Thread t2 = new Thread(new PriorityThread(), "线程B");
Thread t3 = new Thread(new PriorityThread(), "线程C");
t1.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.NORM_PRIORITY);
t3.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
t3.start();
}
}
class PriorityThread implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
//---------------运行结果---------------------
线程A
线程C
线程B
//---------------运行结果---------------------
线程B
线程A
线程C
//---------------运行结果---------------------
线程A
线程B
线程C
看到此运行结果,你一定很意外,小编也很意外,我以为会是ABC顺序执行,没想到我多点几次会出现3种情况,但是大多数情况下都是A,之后是B出现几率比较大,C运行在前面机会最小。因此即使是设置了优先级,也不能保证该线程一定是最先执行,只能说相对低优先级的线程来说高优先级的线程会优先执行。上述结果可能还会有其他的结果,小编猜测应该是ABC自由组合的结果都有可能,读者可以去尝试一下。
Daemon
在Java程序中,只要前台有一个线程在运行,则整个Java进程都不会消失,所以此时可以设置一个后台线程,这样即使Java进程结束了,此后台线程依然会执行。要想实现这样的操作,直接使用setDaemon()方法即可。
Java有两种Thread:“守护线程Daemon”(守护线程)与“用户线程User”(非守护线程)。
**用户线程:**非守护线程包括常规的用户线程或诸如用于处理GUI事件的事件调度线程,Java虚拟机在它所有非守护线程已经离开后自动离开。
**守护线程:**守护线程则是用来服务用户线程的,比如说GC线程。如果没有其他用户线程在运行,那么就没有可服务对象,也就没有理由继续下去。(操作系统里面是没有所谓的守护线程的概念,只有守护进程一说,但是Java语言机制是构建在JVM的基础之上的,意思是Java平台把操作系统的底层给屏蔽起来,所以它可以在它自己的虚拟的平台里面构造出对 自己有利的机制,而语言或者说平台的设计者多多少少是受到Unix思想的影响,而守护线程机制又是对JVM这样的平台凑合,于是守护线程应运而生)。
守护线程使用的情况较少,但并非无用,举例来说,JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。
守护线程与用户线程的唯一区别是:其实User Thread线程和Daemon Thread守护线程本质上来说去没啥区别的,唯一的区别之处就在虚拟机的离开,当JVM中所有的线程都是守护线程的时候,JVM就可以退出了(如果User Thread全部撤离,那么Daemon Thread也就没啥线程好服务的了,所以虚拟机也就退出了);如果还有一个或以上的非守护线程则不会退出。(以上是针对正常退出,调用System.exit则必定会退出)。
如何创建守护线程
守护线程与普通线程写法上基本没什么区别,调用线程对象的方法setDaemon(true),则可以将其设置为守护线程。
1、thread.setDaemon(true)必须在thread.start()之前设置,你不能把正在运行的常规线程设置为守护线程,否则会抛出IllegalThreadStateException异常,如果线程是守护线程,则isDaemon方法返回true。
2、在Daemon线程中产生的新线程也是Daemon的。
3、不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑。因为在Daemon Thread还没来的及进行操作时,虚拟机可能已经退出了。
public final void setDaemon(boolean on)
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。
该方法首先调用该线程的 checkAccess 方法,且不带任何参数。这可能抛出 SecurityException。
参数:
on - 如果为 true,则将该线程标记为守护线程。
抛出:
IllegalThreadStateException - 如果该线程处于活动状态。
SecurityException - 如果当前线程无法修改该线程。
另请参见:
isDaemon(), checkAccess()
public class Daemon {
public static void main(String[] args) {
Thread t1 = new CommonThread();
Thread t2 = new Thread(new DaemonThread());
t2.setDaemon(true); // 设置为守护线程
t2.start();
t1.start();
}
}
class CommonThread extends Thread {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()+" 用户线程第 " + i + " 次执行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class DaemonThread implements Runnable {
public void run() {
for (long i = 0; i < 9999999L; i++) {
System.out.println(Thread.currentThread().getName()+" 守护线程第 " + i + " 次执行!");
try {
Thread.sleep(7);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//---------------运行结果---------------------
//用户线程退出,守护线程就没有存在的意义了
Thread-0 用户线程第 0 次执行!
Thread-1 守护线程第 0 次执行!
Thread-0 用户线程第 1 次执行!
Thread-1 守护线程第 1 次执行!
Thread-0 用户线程第 2 次执行!
Thread-1 守护线程第 2 次执行!
Thread-1 守护线程第 3 次执行!
Thread-0 用户线程第 3 次执行!
Thread-1 守护线程第 4 次执行!
Thread-0 用户线程第 4 次执行!
Thread-1 守护线程第 5 次执行!
线程的中断
一个线程可以被另一个线程中断其操作的状态,使用interrupt()方法。
public class Interrupt {
public static void main(String[] args) {
InterruptThread m = new InterruptThread();
Thread t = new Thread(m, "自定义线程");
t.start();
try {
//sleep方法会出现异常
Thread.sleep(100);
} catch (InterruptedException e) {
}
// t.interrupt();
}
}
class InterruptThread implements Runnable{
@Override
public void run() {
try {
//sleep方法会出现异常
System.out.println("1、进入run方法");
Thread.sleep(1000); //程序会暂停1秒再执行
System.out.println("2、已经完成了休眠");
} catch (InterruptedException e) {
System.out.println("3、休眠被终止!");
return; //返回方法调用处
}
System.out.println("4、run方法正常结束");
}
}
//---------------运行结果---------------------
//正常情况下运行结果 -- 不调用 interrupt() 方法
1、进入run方法
2、已经完成了休眠
4、run方法正常结束
//异常情况下运行结果 -- 调用 interrupt() 方法
1、进入run方法
3、休眠被终止!
终止线程
有三种方法可以使终止线程。
1、使用退出标志,使线程正常退出,也就是当run()
方法完成后线程终止。
2、使用stop()
方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。
3、使用interrupt()
方法中断线程。