Java多线程就是这么简单----API
写在前面:多线程编程是java必须要掌握的技能之一,没有系统地学习多线程,只凭一些零零散散的知识,无法应用于日常的开发中,决定从头开始系统地学习一下线程相关知识,从api开始,翻过这座大山
java.lang.Thread#currentThread
Returns a reference to the currently executing thread object.
返回当前正在执行的线程对象,直接上代码:
public class CurrentThreadTest {
public static void main(String[] args) {
System.out.println("Thread.currentThread().getName():" + Thread.currentThread().getName());
}
}
这里选择直接返回当前线程的名字
Thread.currentThread().getName():main
Process finished with exit code 0
这里打印出了线程的名字:main,这里并不是因为在main函数里的关系,他们仅仅名字一样,下一个例子
public class CurrentThreadTest {
public static void main(String[] args) {
System.out.println("Thread.currentThread().getName():" + Thread.currentThread().getName());
Thread a = new A();
a.setName("a");
a.start();
}
}
class A extends Thread {
public A() {
System.out.println("Thread.currentThread().getName():" + Thread.currentThread().getName());
System.out.println("this.getName():" + this.getName());
}
public void run() {
System.out.println("Thread.currentThread().getName():" + Thread.currentThread().getName());
System.out.println("this.getName():" + this.getName());
}
}
这里选择打印当前线程的名字和线程的名字,下面是输出
Thread.currentThread().getName():main
Thread.currentThread().getName():main
this.getName():Thread-0
Thread.currentThread().getName():a
this.getName():a
Process finished with exit code 0
首先打印了主线程的名字,main,创建线程A实例的时候并没有设置线程实例的名字,这个构造函数被主线程调用,因为打印出了主线程的名字,然后打印出了当前线程的默认名字,即Thread-0,在线程start之前,设置了线程a的名字,因此run方法里会打印出a线程的名字,这个方法是被a拿去执行,this也指向的是a线程
java.lang.Thread#isAlive
Tests if this thread is alive. A thread is alive if it has been started and has not yet died.
查看线程是否还“活”着,当线程处于准备运行或者正在运行的状态时,线程就被判断为活的
public class IsAliveTest {
public static void main(String[] args) throws InterruptedException {
B b = new B();
b.setName("b");
Thread t = new Thread(b);
t.setName("thread");
t.start();
Thread.sleep(1000);
System.out.println("thread:" + t.getName() + " is alive:" + t.isAlive());
}
}
class B extends Thread {
public B() {
System.out.println("constructor--start");
System.out.println("thread:" + Thread.currentThread().getName() + " is alive:" + Thread.currentThread().isAlive());
System.out.println("thread:" + this.getName() + " is alive:" + this.isAlive());
System.out.println("constructor--end");
}
public void run() {
System.out.println("run--start");
System.out.println("thread:" + Thread.currentThread().getName() + " is alive:" + Thread.currentThread().isAlive());
System.out.println("thread:" + this.getName() + " is alive:" + this.isAlive());
System.out.println("run--end");
}
}
输出:
constructor--start
thread:main is alive:true
thread:Thread-0 is alive:false
constructor--end
run--start
thread:thread is alive:true
thread:b is alive:false
run--end
Thread:thread is alive:false
Process finished with exit code 0
首先创建一个b线程,可以看到调用B构造方法的主线程是活的,b线程是死的,接下来用b对象来实例化一个Thread对象t,启动t线程发现线程执行run方法的过程中t线程是活的,因为没有调用b线程的start方法,所以b线程一直是死的,等run方法执行完毕,发现t线程也死了
java.lang.Thread#sleep(long)
Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers. The thread does not lose ownership of any monitors.
是当前线程,即Thread.currentThread()返回的线程睡眠一定毫秒,这个时间受系统的影响。这个方法不会使线程丢失监视器。
public class SleepTest {
public static void main(String[] args) {
Thread t = new C();
System.out.println("main--start:" + System.currentTimeMillis());
t.start();
System.out.println("main--end:" + System.currentTimeMillis());
}
}
class C extends Thread{
public void run(){
try {
System.out.println("run--start:" + System.currentTimeMillis());
Thread.sleep(1000);
System.out.println("run--end:" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
输出
main--start1591449639792
main--end1591449639793
run--start:1591449639793
run--end:1591449640798
Process finished with exit code 0
因为sleep方法会让当前线程睡眠,所以run的start和end间隔了一秒,因为main线程和t线程是异步执行的,所以main的end时间和run的start时间很近
java.lang.Thread#interrupt
Interrupts this thread.
中断这个线程,这个函数可以讨论的东西很多,在线程a中调用线程b的interrupt()方法,即会向线程b发出信号——线程中断状态已被设置。但是线程b究竟怎么样了,就要看具体实现了。
java.lang.Thread#interrupted 这个方法是个静态方法,可以看到线程a的中断状态(暂时没有体会到这个方法的作用,后面知道了再来补上吧)
补:这个线程会清除掉线程的中断状态,而isInterrupted()不会,线程中断后,第一次调用interrupted会返回true表示线程的状态码为true,接下来都会返回false,因为状态被清除掉了,贴出代码:
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
public boolean isInterrupted() {
return isInterrupted(false);
}
//这个是native方法
private native boolean isInterrupted(boolean ClearInterrupted);
补:一段代码解释interrupted
import java.util.concurrent.TimeUnit;
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (true) {
if(Thread.currentThread().interrupted())
System.out.println("我被中断过");
}
});
t.start();
TimeUnit.SECONDS.sleep(1);
t.interrupt();
}
}
输出里只显示了一次我被中断过,说明线程被中断过,当线程遇到可中断的阻塞方法的时候,调用中断方法会打破线程的阻塞状态,然后继续清楚中断标志,继续执行
java.lang.Thread#isInterrupted() 这个方法可以查看当前线程的中断状态,调用b.interrupt()后,b线程的中断状态会被设置为true,但是并不代表线程b就被中断了,文字描述太空泛,直接看代码咯:
public class InterruptedTest {
public static void main(String[] args) throws InterruptedException {
D d = new D();
d.start();
Thread.sleep(5000);
d.setOn(false);
}
}
class D extends Thread {
private boolean on = true;
public void setOn(boolean on) {
this.on = on;
}
public void run() {
while (on) {
try {
Thread.sleep(1000);
System.out.println(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
让线程停止运行,就在线程上加一个开关,打破线程的执行条件,就可以关闭线程,但是这样会带来一个问题:万一线程处于阻塞状态,关闭开关不能及时地中断线程,如下所示:
public class InterruptedTest {
public static void main(String[] args) throws InterruptedException {
D d = new D();
d.start();
Thread.sleep(1000);
d.setOn(false);
}
}
class D extends Thread {
private boolean on = true;
public void setOn(boolean on) {
this.on = on;
}
public void run() {
while (on) {
try {
Thread.sleep(5000);
System.out.println(System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
这里将开关关闭后,仍然打印出了当前时间戳,表明开关并不会及时生效的,这个时候就需要interrupt()来救场了,这里用interrupt()中断了线程之后,线程中断状态标志位被清除(不是设置为false),所以之后不管怎么操作都是 false了
public class InterruptedTest {
public static void main(String[] args) throws InterruptedException {
D d = new D();
d.start();
Thread.sleep(1000);
d.interrupt();
}
}
class D extends Thread {
private boolean on = true;
public void setOn(boolean on) {
this.on = on;
}
public void run() {
while (on) {
try {
Thread.sleep(5000);
System.out.println(System.currentTimeMillis());
} catch (InterruptedException e) {
System.out.println("中断异常");
return;
}
}
}
}
这个方法的改变线程的中断状态,他并不会直接中断线程,但是如果线程被sleep,join或者wait等方法阻塞的时候,interrupt()方法会将状态清除并抛出异常,catch块直接返回就中断了这个线程,下面的代码展示了interrupt()改变状态:
public class InterruptedTest {
public static void main(String[] args) throws InterruptedException {
E e = new E();
e.start();
Thread.sleep(2000);
e.interrupt();
}
}
class E extends Thread {
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()) {
System.out.println("被中断了,但还是在运行");
} else {
System.out.println("没有被中断,一直运行着");
}
}
}
}
java.lang.Thread#yield
静态native方法,方法的作用是放弃当前的CPU资源,将它让给其他的任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。
java.lang.Thread#setPriority
设置线程的优先级,线程的优先级分为1~10共十个等级,如果小于1或者大于10,会抛出IllegalArgumentException异常,jdk的三个优先级如下
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
线程a比线程b优先级高,并不代表线程a完全执行完毕后才执行线程b
public class PriorityTest {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
G g1 = new G(i);
g1.setPriority(10);
g1.start();
G g2 = new G(i);
g2.setPriority(1);
g2.start();
}
}
}
class G extends Thread {
int i;
public G(int i) {
this.i = i;
}
public void run() {
Long start = System.currentTimeMillis();
for (int i = 0; i < 50000; i++) {
double x = Math.random();
}
Long end = System.currentTimeMillis();
System.out.println("第" + i + "回合:优先级" + this.getPriority() + "总用时:" + (end - start));
}
}
可以看到虽然线程1的优先级比线程2的优先级高,但是也会有线程2比线程1先执行完的情况:比如第五回合
第3回合:优先级10总用时:113
第1回合:优先级10总用时:153
第0回合:优先级1总用时:154
第4回合:优先级1总用时:172
第2回合:优先级1总用时:175
第5回合:优先级10总用时:179
第0回合:优先级10总用时:184
第3回合:优先级1总用时:187
第1回合:优先级1总用时:187
第2回合:优先级10总用时:188
第4回合:优先级10总用时:188
第5回合:优先级1总用时:171
第6回合:优先级1总用时:155
第7回合:优先级10总用时:138
第8回合:优先级10总用时:105
第6回合:优先级10总用时:180
第9回合:优先级10总用时:102
第8回合:优先级1总用时:104
第9回合:优先级1总用时:101
第7回合:优先级1总用时:115
Process finished with exit code 0
守护线程
守护线程是一种特殊的线程,它的特性有“陪伴”的含义,当进程中不存在非守护线程了,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有非守护线程了,则垃圾回收线程也就没有存在的必要了,自动销毁。用个比较通俗的比喻来解释一下“守护线程”:任何一个守护线程都是整个JVM中所有非守护线程的“保姆”,只要当前JVM实例中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才随着JVM一同结束工作。Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是GC(垃圾回收器),它就是一个很称职的守护者。
public class DaemonTest {
public static void main(String[] args) throws InterruptedException {
H h = new H();
h.setDaemon(true);
h.start();
Thread.sleep(5000);
System.out.println("主线程结束,守护线程也就结束了");
}
}
class H extends Thread {
public void run() {
while (true) {
System.out.println("守护线程还活着");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("中断异常");
}
}
}
}
输出如下,如果不设置线程h为守护线程,那么线程h会一直打印”守护线程还活着“这句话
守护线程还活着
守护线程还活着
守护线程还活着
守护线程还活着
守护线程还活着
主线程结束,守护线程也就结束了
Process finished with exit code 0