线程的主要方法都定义在 Thread类 之中,因此此处就是学习Thread中的方法。
public Thread(Runnable target, String name)
public final synchronized void setName(String name)
public final String getName()
class MyThread implements Runnable{
@Override
public void run() {
// MyThread 代表着 Runnable 子类,是一类资源,并不是线程,因此不能使用this.getName() 并且也没有这个方法
// 因此要使用 Thread 的类方法,获得当前线程
System.out.println(Thread.currentThread().getName());
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread(); // Runnable 子类对象
new Thread(mt, "线程A").start();
new Thread(mt).start();
new Thread(mt, "线程B").start();
}
}
结果:
线程A
线程B
Thread-0
总结: 说明开发者设置名字的线程返回名字,未设置名字的线程自动分配名字。主要是通过 static 属性来完成的,在 Thread 类中定义有如下操作
/* For autonumbering anonymous threads. */
private static int threadInitNumber;
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
class MyThread implements Runnable{
@Override
public void run() {
// MyThread 代表着 Runnable 子类,是一类资源,并不是线程,因此不能使用this.getName() 并且也没有这个方法
// 因此要使用 Thread 的类方法,获得当前线程
System.out.println(Thread.currentThread().getName());
}
}
public class ThreadDemo {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread(); // Runnable 子类对象
new Thread(mt, "线程A").start();
mt.run(); // 对象直接调用 run() 方法
}
}
结果:
main
线程A
总结:
run()
方法,得到的名称是 “main”,即——主方法也是一个线程;例如,如下程序会在执行任务一之后,发生轻微卡顿。
public class ThreadDemo {
public static void main(String[] args){
System.out.println("1. 执行任务一");
int temp = 0;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
temp += i;
}
System.out.println("2. 执行任务二");
System.out.println("3. 执行任务三");
}
}
将任务一放在子线程中进行,运行情况明显改善
public class ThreadDemo {
public static void main(String[] args){
System.out.println("1. 执行任务一");
new Thread(()->{ // 子线程负责统计
int temp = 0;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
temp += i;
}
});
System.out.println("2. 执行任务二");
System.out.println("3. 执行任务三");
}
}
public static native void sleep(long millis) throws InterruptedException;
public static void sleep(long millis, int nanos)throws InterruptedException;
public class ThreadDemo {
public static void main(String[] args){
new Thread(()->{ // 子线程负责统计
for (int i = 0; i < 10 ; i++) {
System.out.println(Thread.currentThread().getName() + temp);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}).start();;
}
}
在之前发现线程的休眠里面提供有一个中断异常,实际上就证明线程的休眠是可以被打断的,而这种打断肯定是由其它线程完成的。
public boolean isInterrupted()
public void interrupt()
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException{
Thread th = new Thread(()->{
System.out.println("休眠中...");
try {
Thread.sleep(10000); // 子线程休眠10s
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("休眠结束!");
});
th.start();
Thread.sleep(1000); // 主线程休眠1s后中断子线程
if (!th.isInterrupted()) { // 判断 th 子线程是否被中断
th.interrupt(); // 中断 th 子线程
System.out.println("子线程被主线程所中断。。");
}
}
}
结果:
休眠中...
子线程被主线程所中断。。
java.lang.InterruptedException: sleep interrupted
at java.base/java.lang.Thread.sleep(Native Method)
at ThreadDemo.lambda$0(ThreadDemo.java:7)
at java.base/java.lang.Thread.run(Thread.java:833)
休眠结束!
所有正在执行的线程都是可以被中断的,中断线程必须进行异常的处理。
强制执行:当满足于某些条件之后,某一个线程对象将可以一直独占资源,一直到该线程的程序执行结束。
正常情况下,以主线程和子线程为例,两者都在交替执行着
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException{
Thread th = new Thread(() -> {
for(int i = 0; i < 10; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行,i = " + i);
}
}, "正常的子线程");
th.start();
for(int i = 0; i < 10; i++) {
Thread.sleep(100);
System.out.println("正常的主线程,执行,i = " + i);
}
}
}
结果:
正常的子线程执行,i = 0
正常的主线程,执行,i = 0
正常的子线程执行,i = 1
正常的主线程,执行,i = 1
正常的子线程执行,i = 2
正常的主线程,执行,i = 2
正常的主线程,执行,i = 3
正常的子线程执行,i = 3
但是如果说现在你希望主线程独占执行。那么就可以利用 Thread 类中的方法:
public final void join() throws InterruptedException
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException{
Thread th = new Thread(() -> {
for(int i = 0; i < 5; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行,i = " + i);
}
}, "优先的子线程");
th.start();
for(int i = 0; i < 5; i++) {
Thread.sleep(100);
if(i == 1)
th.join(); // 主线程执行1次后,子线程独占资源,直到执行完毕
System.out.println("被迫等待的主线程,执行,i = " + i);
}
}
}
结果:
优先的子线程执行,i = 0
被迫等待的主线程,执行,i = 0
优先的子线程执行,i = 1
优先的子线程执行,i = 2
优先的子线程执行,i = 3
优先的子线程执行,i = 4
被迫等待的主线程,执行,i = 1
被迫等待的主线程,执行,i = 2
被迫等待的主线程,执行,i = 3
被迫等待的主线程,执行,i = 4
线程的礼让指的是先将资源让出去让别的线程先执行。线程的礼让可以使用 Thread 中提供的方法;
public static native void yield(); // 这是一个静态方法
yield()
方法都只会礼让一次当前的资源。join()
的基础上,让强占资源的子线程通过 yield()
让出线程public class ThreadDemo {
public static void main(String[] args) throws InterruptedException{
Thread th = new Thread(() -> {
for(int i = 0; i < 5; i++) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行,i = " + i);
// 当执行到第二次时进行一次礼让
if(i > 1){
Thread.yield();
System.out.println("------礼让执行------");
}
}
}, "优先的子线程");
th.start();
for(int i = 0; i < 5; i++) {
Thread.sleep(100);
if(i == 1)
th.join(); // 主线程执行1次后,子线程独占资源,直到执行完毕
System.out.println("被迫等待的主线程,执行,i = " + i);
}
}
}
结果:
被迫等待的主线程,执行,i = 0
优先的子线程执行,i = 0
优先的子线程执行,i = 1
优先的子线程执行,i = 2
------礼让执行------
优先的子线程执行,i = 3
------礼让执行------
优先的子线程执行,i = 4
------礼让执行------
被迫等待的主线程,执行,i = 1
被迫等待的主线程,执行,i = 2
被迫等待的主线程,执行,i = 3
被迫等待的主线程,执行,i = 4
通过观察结果我们发现,并没有实现预期的效果:
子线程礼让,被迫等待main线程执行。
通过查阅资料后,我们发现了 join()
和 yield()
方法对应于操作系统的细节:
我们知道操作系统对于进程和线程的调度,有五大状态之分:
而对于上述的两个方法而言:
yield()
是将当前线程从运行状态转换到就绪状态,而不能是等待或者阻塞状态。操作系统会从就绪态中的线程重新调度(当然运气好的话,你刚让出来又被调进去,就会出现礼让无效果的情况)join()
方法在线程实例中被调用,当前运行的线程会被堵塞,当前线程变为堵塞态,直到线程实例运行完成。join()
所堵塞的main线程,其本身的状态就不在就绪态之中,所以即使子线程使用 yield()
礼让,操作系统也会从就绪态的线程中选择调用,而不会运行处于堵塞态的main线程。从理论上来讲线程的优先级越高越有可能先执行(越有可能先抢占到资源)。在 Thread 类里面针对于优先级的操作提供有如下的两个处理方法:
public final void setPriority(int new Priority)
public final int getPriority()
public static final int MAX PRIORITY;
——10public static final int NORMPRIORITY;
——5public static final int MIN PRIORITY;
——1