一、线程周期
二、线程的调度
①sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
②join():指等待t线程终止。
使用方式。
join是Thread类的一个方法,启动线程后直接调用,即join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
Thread t = new AThread(); t.start(); t.join();
为什么要用join()方法
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了。
不加join。
/**
*@functon 多线程学习,join
*/
package com.multithread.join;
class Thread1 extends Thread{
private String name;
public Thread1(String name) {
super(name);
this.name=name;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程运行开始!");
for (int i = 0; i < 5; i++) {
System.out.println("子线程"+name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程运行结束!");
}
}
public class Main {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"主线程运行开始!");
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");
}
}
输出结果:
main主线程运行开始!
main主线程运行结束!
B 线程运行开始!
子线程B运行 : 0
A 线程运行开始!
子线程A运行 : 0
子线程B运行 : 1
子线程A运行 : 1
子线程A运行 : 2
子线程A运行 : 3
子线程A运行 : 4
A 线程运行结束!
子线程B运行 : 2
子线程B运行 : 3
子线程B运行 : 4
B 线程运行结束!
发现主线程比子线程早结束
加join
public class Main {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"主线程运行开始!");
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
try {
mTh1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
mTh2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");
}
}
运行结果:
main主线程运行开始!
A 线程运行开始!
子线程A运行 : 0
B 线程运行开始!
子线程B运行 : 0
子线程A运行 : 1
子线程B运行 : 1
子线程A运行 : 2
子线程B运行 : 2
子线程A运行 : 3
子线程B运行 : 3
子线程A运行 : 4
子线程B运行 : 4
A 线程运行结束!
主线程一定会等子线程都结束了才结束
③、yield():暂停当前正在执行的线程对象,并执行其他线程。
Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。
yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。可看上面的图
package com.multithread.yield;
class ThreadYield extends Thread{
public ThreadYield(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println("" + this.getName() + "-----" + i);
// 当i为30时,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
if (i ==30) {
this.yield();
}
}
}
}
public class Main {
public static void main(String[] args) {
ThreadYield yt1 = new ThreadYield("张三");
ThreadYield yt2 = new ThreadYield("李四");
yt1.start();
yt2.start();
}
}
第一种情况:李四(线程)当执行到30时会CPU时间让掉,这时张三(线程)抢到CPU时间并执行。
第二种情况:李四(线程)当执行到30时会CPU时间让掉,这时李四(线程)抢到CPU时间并执行。
sleep() yield()的区别
sleep()和yield()的区别:sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep()方法是当前运行中的线程睡眠一段时间,进入不可运行状态,这段时间的长短是由程序设定的,yield()方法使当前线程让出CPU占有权,但让出的时间是不可设定的。实际上,yield()方法对应了如下操作:先检测当前是否有相同优先级的线程处于同时可运行状态,如有,则把CPU的占有权交给此线程,否则,继续运行原来的线程。所以yield()方法称为”退让“,它把运行机会让给了同等优先级的其他线程。
另外,sleep()方法允许较低优先级的线程获得运行机会,但yield()方法执行时,当前线程人处于可运行状态,所有,不可能让出较低优先级的线程获得CPU占有权。在一个运行系统中,如果较高优先级的线程没有调用sleep()方法,有没有受到I/O堵塞,那么,较低优先级线程只能等待所有较高优先级的线程运行结束,才有机会运行。
④、setPriority():更改线程的优先级。
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
用法:
⑤、interrupt():中断某个线程,这种结束方式比较粗暴,如果t线程打开了某个资源还没来得及关闭也就是run()方法还没有执行完就强制结束线程,会导致资源无法关闭。
要想结束进程最好的办法就是sleep()函数的例子程序里那样,在线程类里面用一个boolean型变量来控制run()方法什么时候结束,run()方法一结束,该线程也就结束了。
⑥、wait()
obj.wait(),与obj.notify()必须要与synchronized(obj) 一起使用,也就是wait。与notify是针对已经获取了obj锁进行操作,从语法角度来说就是obj.wait(),obj.notify()必须在synchronized(obj){...}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其他线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与obj.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于obj.wait()在释放CPU同时,释放了对象锁的控制。
具体的例子说明该知识点:
建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC
输出结果:
ABCABCABCABCABCABCABCABCABCABC
wait和sleep区别:
共同点:
1、他们都是在多线程的环境下,都可以在程序的调用处堵塞指定的毫秒数,并返回。
2、wait() sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptException。
如果线程A希望立刻结束线程B,则可以对线程B对应的Thread实例调用interrupt方法,如果此刻B正在wait/sleep/join,则线程B会立刻抛出InterruptException,在catch(){}中直接return即可安全地结束线程。需要注意的是,InterruptException是线程自己从内部抛出的,并不是Interrupt()方法抛出的。对某一线程调用Interrupt(0时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptException。但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptException。
不同点:
1、Thread类的方法:sleep(),yield()等
Object的方法:wait()和notify()等
2、每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
4、sleep必须捕获异常,而wait,notify,notifyAll不需要捕获异常
所以sleep() wait()方法的最大区别是:
sleep睡眠时,保持对象锁,仍然占有该锁;
而wait睡眠时,释放对象锁。
但是wait和sleep都可以通过Interrupt方法打断线程的暂停状态,从而使线程立刻抛出InterruptException(不建议使用该方法)。
sleep()方法
sleep使当前线程进入停滞状态(堵塞当前线程),让出CPU的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;
sleep是Thread类的static的方法;因此它不能改变对象的机锁,所以当在一个synchronized块中调用sleep方法是,线程虽然休眠了,但是对象的机锁并没有被释放,其他线程无法访问这个对象(即使睡眠张也持有对象锁)。
在sleep休眠时间期满后,该线程不一定会立即执行,这是因为其他线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。
wait()方法
wait方法是object类里的方法;当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超过时间到后还需要返还对象锁);其他线程可以访问;
wait使用notify或者notifyAll或者指定睡眠时间来唤醒当前等待池中的线程。
wait必须放在synchronized block中,否则会在program runtime时扔出”java.long.lllegalMonitorStateException“异常。
四、常见线程名词解释
主线程:JVM调用程序main()所产生的线程。
当前线程:这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束
前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。
线程类的一些常用方法:
sleep():强迫一个线程睡眠N毫秒
isAlive():判断一个线程是否存活
join():等待线程终止
activeCount():程序中活跃的线程数
enumerate():枚举程序中的线程
currentThread():得到当前线程
isDaemon():一个线程是否为守护线程
setDaemon():设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
setName():为线程设置一个名称
wait():强迫一个线程等待
notify():通知一个线程继续运行
setPriority():设置一个线程的优先级
五、线程同步
sleep() yield()的区别
sleep()和yield()的区别:sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep()方法是当前运行中的线程睡眠一段时间,进入不可运行状态,这段时间的长短是由程序设定的,yield()方法使当前线程让出CPU占有权,但让出的时间是不可设定的。实际上,yield()方法对应了如下操作:先检测当前是否有相同优先级的线程处于同时可运行状态,如有,则把CPU的占有权交给此线程,否则,继续运行原来的线程。所以yield()方法称为”退让“,它把运行机会让给了同等优先级的其他线程。
另外,sleep()方法允许较低优先级的线程获得运行机会,但yield()方法执行时,当前线程人处于可运行状态,所有,不可能让出较低优先级的线程获得CPU占有权。在一个运行系统中,如果较高优先级的线程没有调用sleep()方法,有没有受到I/O堵塞,那么,较低优先级线程只能等待所有较高优先级的线程运行结束,才有机会运行。
④、setPriority():更改线程的优先级。
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
用法:
⑤、interrupt():中断某个线程,这种结束方式比较粗暴,如果t线程打开了某个资源还没来得及关闭也就是run()方法还没有执行完就强制结束线程,会导致资源无法关闭。
要想结束进程最好的办法就是sleep()函数的例子程序里那样,在线程类里面用一个boolean型变量来控制run()方法什么时候结束,run()方法一结束,该线程也就结束了。
⑥、wait()
obj.wait(),与obj.notify()必须要与synchronized(obj) 一起使用,也就是wait。与notify是针对已经获取了obj锁进行操作,从语法角度来说就是obj.wait(),obj.notify()必须在synchronized(obj){...}语句块内。从功能上来说wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其他线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操作。但有一点需要注意的是notify()调用后,并不是马上就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操作。Thread.sleep()与obj.wait()二者都可以暂停当前线程,释放CPU控制权,主要的区别在于obj.wait()在释放CPU同时,释放了对象锁的控制。
具体的例子说明该知识点:
建立三个线程,A线程打印10次A,B线程打印10次B,C线程打印10次C,要求线程同时运行,交替打印10次ABC
输出结果:
ABCABCABCABCABCABCABCABCABCABC
wait和sleep区别:
共同点:
1、他们都是在多线程的环境下,都可以在程序的调用处堵塞指定的毫秒数,并返回。
2、wait() sleep()都可以通过interrupt()方法打断线程的暂停状态,从而使线程立刻抛出InterruptException。
如果线程A希望立刻结束线程B,则可以对线程B对应的Thread实例调用interrupt方法,如果此刻B正在wait/sleep/join,则线程B会立刻抛出InterruptException,在catch(){}中直接return即可安全地结束线程。需要注意的是,InterruptException是线程自己从内部抛出的,并不是Interrupt()方法抛出的。对某一线程调用Interrupt(0时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptException。但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptException。
不同点:
1、Thread类的方法:sleep(),yield()等
Object的方法:wait()和notify()等
2、每个对象都有一个锁来控制同步访问。Synchronized关键字可以和对象的锁交互,来实现线程的同步。sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用
4、sleep必须捕获异常,而wait,notify,notifyAll不需要捕获异常
所以sleep() wait()方法的最大区别是:
sleep睡眠时,保持对象锁,仍然占有该锁;
而wait睡眠时,释放对象锁。
但是wait和sleep都可以通过Interrupt方法打断线程的暂停状态,从而使线程立刻抛出InterruptException(不建议使用该方法)。
sleep()方法
sleep使当前线程进入停滞状态(堵塞当前线程),让出CPU的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留一定时间给其他线程执行的机会;
sleep是Thread类的static的方法;因此它不能改变对象的机锁,所以当在一个synchronized块中调用sleep方法是,线程虽然休眠了,但是对象的机锁并没有被释放,其他线程无法访问这个对象(即使睡眠张也持有对象锁)。
在sleep休眠时间期满后,该线程不一定会立即执行,这是因为其他线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。
wait()方法
wait方法是object类里的方法;当一个线程执行到wait方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超过时间到后还需要返还对象锁);其他线程可以访问;
wait使用notify或者notifyAll或者指定睡眠时间来唤醒当前等待池中的线程。
wait必须放在synchronized block中,否则会在program runtime时扔出”java.long.lllegalMonitorStateException“异常。
四、常见线程名词解释
主线程:JVM调用程序main()所产生的线程。
当前线程:这个是容易混淆的概念。一般指通过Thread.currentThread()来获取的进程。
后台线程:指为其他线程提供服务的线程,也称为守护线程。JVM的垃圾回收线程就是一个后台线程。用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束
前台线程:是指接受后台线程服务的线程,其实前台后台线程是联系在一起,就像傀儡和幕后操纵者一样的关系。傀儡是前台线程、幕后操纵者是后台线程。由前台线程创建的线程默认也是前台线程。可以通过isDaemon()和setDaemon()方法来判断和设置一个线程是否为后台线程。
线程类的一些常用方法:
sleep():强迫一个线程睡眠N毫秒
isAlive():判断一个线程是否存活
join():等待线程终止
activeCount():程序中活跃的线程数
enumerate():枚举程序中的线程
currentThread():得到当前线程
isDaemon():一个线程是否为守护线程
setDaemon():设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
setName():为线程设置一个名称
wait():强迫一个线程等待
notify():通知一个线程继续运行
setPriority():设置一个线程的优先级