一 (线程的使用)
(!!!都是Thread类中的方法!!!)
(stop,run,start,sleep,yield,join,this.getName,Thread.currentThread().getName,thread.setName,isAlive)
1 终止线程
(用while循环,把run方法执行的动作写里面,再写一个控制线程停止的方法)
(一般不使用JDK提供的stop()/destroy()方法------>被JDK废弃------>直接杀死,结束没有后续了(不人性化))
(boolean型的终止变量------>置为false时------>线程终止------>(把自己该做的做完再终止))
(Thread.sleep();线程休眠------>立即阻塞,静态方法 , System.in.read();会线程阻塞)
终止线程我们一般不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。通常的做法是提供一个boolean型的终止变量,当这个变量置为false,则终止线程的运行。
终止线程的典型方法
java
public class StopThread implements Runnable {
private boolean flag = true; //相当于生死牌
//重写run方法
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 线程开始");
int i= 0;
while(flag){
System.out.println(Thread.currentThread().getName()+" "+i++);
try {
Thread.sleep(1000); //线程休眠,下面详细讲
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 线程结束");
}
//控制生死牌的方法
public void stop(){
this.flag = false;
}
public static void main(String[] args)throws Exception {
System.out.println("主线程开始");
StopThread st = new StopThread();
Thread t1 = new Thread(st);
t1.start();
System.in.read();
st.stop();
System.out.println("主线程结束");
}
}
2 线程休眠
(Thread.sleep();静态方法------>线程休眠------>立即阻塞------>时间到了,解除阻塞)
(单位毫秒)
(方法会报编译异常
------>sleep方法里面抛出了异常(时间不能为负数)
------>只能try-catch出去不能抛
------>因为接口里的run方法没有抛(子类不能抛比父类范围大的),父类没有抛,子类也不能抛)
sleep()方法:可以让正在运行的线程进入阻塞状态,直到休眠时间满了,进入就绪状态。sleep方法的参数为休眠的毫秒数。
java
public class SleepThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" 线程开始");
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
//线程休眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+" 线程结束");
}
public static void main(String[] args) {
System.out.println("主线程开始");
Thread t = new Thread(new SleepThread());
t.start();
System.out.println("主线程结束");
}
}
3 线程让步
(yield();静态方法------>让在运行的线程------>就绪状态,给相同优先级的其他线程得到运行机会)
(和sleep有着本质的区别------>sleep是让运行状态回到阻塞状态,阻塞解除再就绪状态------>yield是让运行状态回到就绪状态)
yield()让当前正在运行的线程回到就绪状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中
使用yield方法时要注意的几点:
- yield是一个静态的方法。
- 调用yield后,yield告诉当前线程把运行机会交给具有相同优先级的线程。
- yield不能保证,当前线程迅速从运行状态切换到就绪状态。
- yield只能是将当前线程从运行状态转换到就绪状态,而不能是等待或者阻塞状态。
java
public class TestyieldThread implements Runnable {
@Override
public void run() {
for(int i=0;i<30;i++){
//如果第一次执行的是Thread-0,就让步,让步一次
if("Thread-0".equals(Thread.currentThread().getName())){
if(i == 0){
Thread.yield();
}
}
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new TestyieldThread());
Thread t2 = new Thread(new TestyieldThread());
t1.start();
t2.start();
}
}
4 线程联合
(join();方法------>调用谁的join方法,谁结束被联合的,把联合的阻塞)
(联合之前线程之间是并行,联合之后线程之间是串行)
(与!方法的调用!相似------>联合线程等待被联合的线程)
(会有编译异常try-catch掉)
(想做线程的联合必须有线程的对象)
当前线程邀请调用方法的线程优先执行,在调用方法的线程执行结束之前,当前线程不能再次执行。线程A在运行期间,可以调用线程B的join()方法,让线程B和线程A联合。这样,线程A就必须等待线程B执行完毕后,才能继续执行。
join方法的使用
join()方法就是指调用该方法的线程在执行完run()方法后,再执行join方法后面的代码,即将两个线程合并,用于实现同步控制。
主线程联合A线程,A线程再联合B线程
java
//A线程
class A implements Runnable{
//必须要有联合的线程对象
private Thread b;
public A(Thread b){
this.b = b;
}
//重写run
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" A "+i);
//join
if(i == 5){
try {
this.b.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//sleep
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//B线程
class B implements Runnable{
//重写run
@Override
public void run() {
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+" B "+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//主线程
public class TestJoinThread {
public static void main(String[] args) {
Thread t1 = new Thread(new B());
Thread t = new Thread(new A(t1));
t.start();
t1.start();
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
//join
if(i ==2){ //当主线程i为2时,联合线程
try {
t.join(); //在主线程当中联合子线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//sleep
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
5 线程联合案例
需求:
实现爸爸让儿子买烟------>只有儿子把烟买回来了,爸爸抽烟的线程才能继续
java
/**
* 儿子买烟线程
*/
class SonThread implements Runnable{
@Override
public void run() {
System.out.println("儿子出门买烟");
System.out.println("儿子买烟需要10分钟");
//模拟买烟
for(int i=0;i<10;i++){
System.out.println("第"+i+"分钟");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("儿子买烟回来了");
}
}
/**
* 爸爸抽烟线程
*/
class FatherThread implements Runnable{
@Override
public void run() {
System.out.println("爸爸想抽烟,发现烟抽完了");
System.out.println("爸爸让儿子去买一包红塔山");
//启动儿子买烟线程
Thread t = new Thread(new SonThread());
t.start();
System.out.println("等待儿子买烟回来");
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("爸爸出门找儿子"); //如果出异常了,爸爸出去找儿子,结束线程。
System.exit(1); //结束虚拟机
}
System.out.println("爸爸高兴的接过烟,并把零钱给了儿子");
}
}
public class TestJoinDemo {
public static void main(String[] args) {
System.out.println("爸爸和儿子买烟的故事");
Thread t = new Thread(new FatherThread());
t.start();
}
}
6 获取线程名称
(继承Thread------>this.getName())
(实现Runnable------>Thread.currentThread().getName())
(------>Thread.currentThread()静态方法,返回当前线程对象,然后再继续用静态方法得到名字)
方式一
this.getName()获取线程名称,该方法适用于继承Thread实现多线程方式。
java
class GetName1 extends Thread{
@Override
public void run() {
System.out.println(this.getName());
}
}
方式二
Thread.currentThread().getName()获取线程名称,该方法适用于实现Runnable接口实现多线程方式。
java
class GetName2 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
7 修改线程名称
(继承Thread------>提供有参的构造方法,super给Thread------>创建对象时通过参数设置)
(实现Runnable------>创建对象时已经通过Thread类把线程包装了------>之间用thread.setName())
(------>启动线程之前修改名字)
(实现Runnable------>通过Thread的构造方法)
方式一
通过构造方法设置线程名称。
java
class SetName1 extends Thread{
public SetName1(String name){
super(name); //把线程名字交给Thread父类
}
@Override
public void run() {
System.out.println(this.getName());
}
}
public class SetNameThread {
public static void main(String[] args) {
SetName1 setName1 = new SetName1("SetName1"); //创建对象时候给名字参数
setName1.start();
}
}
方式二
通过setName()方法设置线程名称。
java
class SetName2 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class SetNameThread {
public static void main(String[] args) {
Thread thread = new Thread(new SetName2());
thread.setName("SetName2");
thread.start();
}
}
方式三
通过构造方法
java
class SetName3 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public class PriorityThread {
public static void main(String[] args)throws Exception {
SetName3 p1 = new SetName3();
SetName3 p2 = new SetName3();
Thread t1 = new Thread(p1,"线程1");
Thread t2 = new Thread(p2,"线程2");
t1.start();
t2.start();
}
}
8 判断线程是否存活
(isAlive()方法------> 判断当前线程是否活动状态------>就绪,运行,阻塞(除了新生和死亡))
isAlive()方法: 判断当前的线程是否处于活动状态。
活动状态是指线程已经启动且尚未终止,线程处于正在运行或准备开始运行的状态,就认为线程是存活的。
java
class Alive implements Runnable{
@Override
public void run() {
for(int i=0;i<4;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
//sleep
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class TestAliveThread {
public static void main(String[] args) {
//实例化
Thread thread = new Thread(new Alive());
//设置名称
thread.setName("Alive");
//启动之后线程活动
thread.start();
System.out.println(thread.getName()+" "+thread.isAlive());//返回false
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//子线程结束
System.out.println(thread.getName()+" "+thread.isAlive());//返回false
}
}
二 (线程的优先级)
1 什么是优先级
(虽然说在Java有的多线程中,线程优先级的设计的初衷是很好的------>但是最终会受到操作系统的限制,而未必能够达到当前的效果------>与操作系统有关------>所以优先级越高,被执行的!!!概率越高!!!(不是绝对))
(范围1--->10,数值越大优先级越高,数值越小优先级越低)
每一个线程都是有优先级的,我们可以为每个线程定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程的优先级用数字表示,范围从1到10,一个线程的缺省优先级是5。
Java 的线程优先级调度会委托给操作系统去处理,所以与具体的操作系统优先级有关,如非特别需要,一般无需设置线程优先级。
注意
线程的优先级,不是说哪个线程优先执行,如果设置某个线程的优先级高。那就是有可能被执行的概率高。并不是优先执行。
2 优先级的使用
(线程启动之前设置)
(两个方法------>
获得优先级------>int getPriority()
设置优先级------>void setPriority(int newPriority)
(Thread.MAX_PRIORITY------>10, Thread.MIN_PRIORITY------>1)
使用下列方法获得或设置线程对象的优先级。
- int getPriority();
- void setPriority(int newPriority);
**注意:**优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高的线程后调用优先级低的线程。
java
class Priority implements Runnable{
private int num = 0; //计数
private boolean flag = true; //生死牌
@Override
public void run() {
while(this.flag){
System.out.println(Thread.currentThread().getName()+" "+num++);
}
}
//stop方法
public void stop(){
this.flag = false;
}
}
public class PriorityThread {
public static void main(String[] args)throws Exception {
Priority p1 = new Priority();
Priority p2 = new Priority();
Thread t1 = new Thread(p1,"线程1");
Thread t2 = new Thread(p2,"线程2");
System.out.println(t1.getPriority()); //5, 默认线程优先级
//Thread.MAX_PRIORITY = 10
t1.setPriority(Thread.MAX_PRIORITY);
//Thread.MAX_PRIORITY = 1
t2.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
Thread.sleep(1000);
p1.stop();
p2.stop();
}
}
三 (守护线程)
1 什么是守护线程
(Daemon Thread(守护线程)------>服务线程------>为User Thread(用户线程)做服务的)
(用户线程------>只有两种情况会死掉------>在run中异常终止------>正常把run执行完毕,线程死亡)
(守护线程------>随着用户线程的死亡而死亡)
在Java中有两类线程:
- User Thread(用户线程):就是应用程序里的自定义线程。
- Daemon Thread(守护线程):比如垃圾回收线程,就是最典型的守护线程。
守护线程(即Daemon Thread),是一个服务线程,准确地来说就是服务其他的线程,这是它的作用,而其他的线程只有一种,那就是用户线程。
守护线程特点:
- 守护线程会随着用户线程死亡而死亡。
守护线程与用户线程的区别:
-
用户线程,不随着主线程的死亡而死亡。用户线程只有两种情况会死掉,1在run中异常终止。2正常把run执行完毕,线程死亡。
-
守护线程,随着用户线程的死亡而死亡,当用户线程死亡守护线程也会随之死亡。
2 守护线程的使用
(Jvm主线程在运行时,会启动很多的辅助线程,其中有一个辅助线程就是专门做垃圾回收的辅助线程,如果主线程结束,那么这个辅助线程会随之消亡的)
(setDaemon(true)方法------>设置为守护线程------>可以守护任何线程包括子线程)
java
//守护线程
class Daemon implements Runnable{
@Override
public void run() {
for(int i=0;i<20;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//子线程
class UsersThread implements Runnable{
@Override
public void run() {
Thread t = new Thread(new Daemon(),"Daemon");
//将该线程设置为守护线程
t.setDaemon(true);
t.start();
for(int i=0;i<5;i++){
System.out.println(Thread.currentThread().getName()+" "+i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//主线程
public class DaemonThread {
public static void main(String[] args)throws Exception {
Thread t = new Thread(new UsersThread(),"UsersThread");
t.start();
Thread.sleep(1000);
System.out.println("主线程结束");
}
}