一、线程的sleep()、yield()、join()方法
在多线程中,关于资源的方法,都是锁定的方法!!下面的几个方法就是锁定的方法。
1.sleep()---线程休眠方法
线程休眠:让线程暂缓执行一下,等到预计时间之后再恢复执行。线程休眠会交出CPU,并让CPU执行其它的任务。
sleep()不会释放锁,即如果当前线程持有对某个对象的锁,即使调用sleep(),其它线程也无法访问这个对象。(运行->阻塞)
2.yield()---线程让步
暂停当前正在执行的线程对象,并执行其它线程。当前线程不一定交出CPU,具体释放CPU的时间由线程调度决定。如果此时有具有相同优先级的线程处于就绪状态,当前线程会交出CPU权限,让CPU去执行拥有相同优先级的线程。如果没有,则什么都不做。同样不会释放锁。但是yield方法不能控制具体交出CPU的时间。(运行->就绪)
调用yield()不会让线程进入阻塞状态,而是直接回到就绪状态,只需要等待重新获取CPU执行时间。
3.sleep VS yield
同:在当前线程中调用这两个方法,都会使当前线程暂停。执行其它的线程。不会释放锁。
异:sleep()可以控制暂停的时间,而yield()不能控制。
yield()交出的CPU只能让CPU执行拥有相同线程优先级的其它线程。sleep()不考虑优先级。
sleep()会使当前线程先阻塞,然后等待时间结束后才回到就绪状态。而yield()直接使当前线程回到就绪状态。
sleep()抛出InterrupptedException异常,而yield()没有声明抛出任何异常。
4.join()--等待该线程终止
当前线程会阻塞等待其它线程执行完毕后再恢复执行。如果在当前线程(主线程或其它线程)中调用线程对象的join(),那么当前线程阻塞,直到线程对象的run()执行完毕,当前线程阻塞才解除。(运行->阻塞)
package com.xunpu.a;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Demo4{
public static void main(String[] args) throws InterruptedException {
Mythread4 mythread=new Mythread4();
Thread t=new Thread(mythread);
t.start();
t.join();
System.out.println("代码结束");
}
public static void printTime(){
Date date=new Date();
DateFormat format=new SimpleDateFormat("yyyy-mm-dd hh:mm:ss");
String time=format.format(date);
System.out.println(time);
}
}
class Mythread4 implements Runnable {
public void run() {
try {
System.out.println("主线程睡眠开始时间:");
Demo4.printTime();//静态方法,直接通过主类名调用
Thread.sleep(2000);
System.out.println("当前线程:"+Thread.currentThread().getName());
System.out.println("线程结束的时间:");
Demo4.printTime();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
二、线程的停止
1.线程停止的三种方式
1)代码控制:设置标记位,修改标记位,使线程终止。可以让线程正常终止。
2)使用stop()强制使线程退出,但是该方法不安全,已废弃。
stop()为什么不安全?
因为stop()会导致线程不会正确释放资源。stop()会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会立即停止。
3)使用Thread类中的一个interrupt()中断线程。Thread.interrupt():将线程状态设置为中断状态。
interrupt()使线程停止的两种方式:
a.线程中没有wait、sleep、join方法时,调用线程的interrupt()只是将线程状态置为interrupt=true;
b.线程中有wait、sleep、join方法时,调用interrupt()会抛出InterruptException,在catch块中捕获该异常,然后退出。
总结来说,调用线程的interrupt(),只是设置线程的中断标志为true,后续操作根据线程的状态采取不同的操作。如果线程处于阻塞状态(由sleep()、wait()、join()引起),那么设置线程中断标志为true后,还会抛出InterruptedException异常,并清除中断标志,重新设置中断标志为false。interrupt()只是改变中断状态,不会中断一个正在运行的线程。这个方法实际完成的是,给受阻塞的线程发出一个中断信号,这样受阻线程得以退出阻塞的状态。
interrupt()不会立即执行中断操作,只会给线程设置一个为true的中断标志(中断标志只是一个布尔类型的变量)。设置之后,根据线程当前的状态进行后续操作。如果当前线程为非阻塞状态,只修改中断标志为true;如果处于阻塞状态,如果是sleep()、wait()、join()引起,则抛出InterruptedException异常,且中断标志被程序会自动清除,重新设置为true。
2.具体代码实现
package com.xunpu.a;
/**
* 使线程停止的方法
* 方式一:设置标志位
*/
public class Demo5{
public static void main(String[] args) throws InterruptedException {
Mythread5 mythread=new Mythread5();
Thread t=new Thread(mythread);
t.start();
Thread.sleep(2000);
mythread.setFlag(false);
System.out.println("代码结束");
}
}
class Mythread5 implements Runnable {
private boolean flag=true;
public void run() {
while(flag){
System.out.println(Thread.currentThread().getName());
}
}
public void setFlag(boolean flag){
this.flag=flag;
}
}
结果:(前2s一直输出):Thread-0
'r (2s时输出):代码结束
package com.xunpu.a;
/**
* 使线程停止:调用Thread.interrupt()
*/
public class Demo6{
public static void main(String[] args) throws InterruptedException {
Mythread6 mythread=new Mythread6();
Thread t=new Thread(mythread,"子线程A");
t.start();
Thread.sleep(3000);
t.interrupt();
System.out.println("代码结束");
}
}
class Mythread6 implements Runnable {
private boolean flag=true;
public void run() {
int i=1;
while(flag){
try {
Thread.sleep(1000);//sleep引起阻塞
boolean bool=Thread.currentThread().isInterrupted();//中断,bool=true;未中断,bool=false.
//3s时,中断造成的异常立即被捕获到,不再执行try下面的代码。
if(bool){
System.out.println("非阻塞情况下执行该操作。。。线程状态"+bool);
break;
}
System.out.println("第"+i+"次执行,线程名称为:"+Thread.currentThread().getName());
i++;
} catch (InterruptedException e) {
System.out.println("退出了");//在这里会退出阻塞状态,且中断标志被系统自动清除,并重新设置为false.
boolean bool=Thread.currentThread().isInterrupted();//false
System.out.println(bool);
return;
}
}
}
}
结果:(1s时输出):第1次执行,线程名称为:子线程A
(2s时输出):第2次执行,线程名称为:子线程A
(3s时输出):代码结束
退出了
false
三、线程的优先级(1~10)
1.含义
线程的优先级越高,越有可能优先执行,仅仅是有可能而已。
2.与线程优先级有关的几个函数(Thread类提供)
a.设置线程的优先级 public void setPriority(int newPriority);
b.获取线程的优先级 public int getPriority();
3.设置优先级可以通过Thread类的几个常量来决定
a.最高优先级 Thread.MAX_PRIORITY=10
b.中等优先级 Thread.NORM_PRIORITY=5
c.最低优先级 Thread.MIN_PRIORITY=1
说明:1)主方法是中等优先级。一般的线程如果没有设置优先级,那么会默认是5.
2)线程具有继承性。如果在一个线程(父线程)中启动另一个线程(子线程),那么子线程的线程优先级和父线程相同。但是子线程的优先级可以修改。
注意:优先级为10的不一定先执行,这和操作系统有关。在某些操作系统中,优先级并不会生效!!
package com.xunpu.a;
public class Priority {
public static void main(String[] args) {
MyThread7 myThread1=new MyThread7();
Thread t1=new Thread(myThread1,"ThreadA");
Thread t2=new Thread(myThread1,"ThreadB");
Thread t3=new Thread(myThread1,"ThreadC");
t3.setPriority(Thread.MAX_PRIORITY);
t2.setPriority(Thread.NORM_PRIORITY);//5
t1.setPriority(Thread.MIN_PRIORITY);//1
t3.start();
t1.start();
t2.start();
System.out.println(Thread.currentThread().getPriority());//当前线程是主线程,默认是5。
}
}
class MyThread7 implements Runnable{
public void run(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程为:"+Thread.currentThread().getName()+" 优先级为:"+Thread.currentThread().getPriority());
Thread t=new Thread();
t.start();
int priority=t.getPriority();
System.out.println(" 子线程的优先级是:"+priority);
}
}
四、守护线程
1.定义
守护线程是一种特殊的线程,也叫做后台线程。它属于是一种陪伴线程。如: 垃圾回收线程
只要当前JVM进程中存在任何一个非守护线程没有结束,守护线程就在工作;只有当最后一个非守护线程结束时,守护线程才会随着JVM一同停止工作。(生命周期)
2.Java中的两种线程
用户线程和守护线程。可以isDaemon()方法来区别它们:如果返回false,则说明该线程是“用户线程”;否则就是“守护线程”。
package com.xunpu.a;
/**
* 守护进程和用户进程
*/
public class Demo8 {
public static void main(String[] args) throws InterruptedException {
//创建守护线程
// A company=new A();
Thread t1=new Thread(new A(),"线程A");//此时t1默认是用户线程
t1.setDaemon(true);//设置t1线程是守护线程,必须在t1启动之前设置。
t1.start();
//创建用户线程
Thread t2=new Thread(new A(),"线程B");
t2.start();
Thread.sleep(3000);
t2.interrupt();
Thread.sleep(1000);
System.out.println("代码结束");
}
}
class A implements Runnable {
private int i;
public void run() {
while (true) {
try {
i++;
System.out.println("线程名称:" + Thread.currentThread().getName() + ",i=" + i + "是否是守护线程" + Thread.currentThread().isDaemon());
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("线程" + Thread.currentThread().getName() + "中断了");
break;
}
}
}
}
结果:
线程名称:线程B,i=1是否是守护线程false
线程名称:线程A,i=1是否是守护线程true
(1s时输出)线程名称:线程B,i=2是否是守护线程false
线程名称:线程A,i=2是否是守护线程true
(2s时输出)线程名称:线程A,i=3是否是守护线程true
线程名称:线程B,i=3是否是守护线程false
(3s时输出)线程线程B中断了
线程名称:线程A,i=4是否是守护线程true
(4s时输出)代码结束
注意:
1)只有在线程启动前(即调用start()前),才能通过Thread.setDaemon(true)把它设置为后台线程(守护线程)。如果在线程启动后,再调用这个线程的setDaemon()方法,会抛出IllegalThreadStateException异常。
2)由用户线程(也叫做前台线程)创建的线程默认情况下仍然是前台线程,由守护线程(后台线程)创建的线程默认情况下仍然是守护线程(后台线程)。