1.1 进程和多线程的概念以及线程的优点
- 进程: 进程是操作系统进行资源分配和调度的基本单位(可以将windows中任务管理器中的每个exe程序理解为一个进程)
- 线程: 线程可以理解为进行中独立运行的主任务
- 例如: 电脑管家这个程序可以看做一个进程,电脑管家里的病毒查杀,清理垃圾等可以做为线程
单线程与多线程的区别:
相对于单线程,多线程可以同时处理多个任务,减少总的任务完成时间,这是多线程的优点,使用多线程可以理解为在使用异步.
注意: 多线程是异步的,所以千万不要把创建线程的顺序理解为线程的执行顺序,线程被调用的时机是随机的
1.2 使用多线程
一个进程在运行是至少有一个线程在运行,比如调用的main线程,是有jvm创建的.
注意: 在控制台输出的main与main方法没有任何关系,只是名字相同
1.2.1 继承Thread类
使用继承thread的方式创建线程时,最大的局限就是不支持多继承了,因为java规定类只能单继承,所以,一般推荐使用实现Runnable的方式创建线程.
执行的结果:
从结果可以看出,代码的运行结果与代码执行顺序或者调用顺序无关的
线程是一个子任务,CPU以不确定的方式,或随机的时间来调用线程中的run方法,所以才会出现上面这种结果与执行顺序不同的情况.
注意: 如果多次调用start(), 可能会出现异常: Exception in thread “main” java.lang.IllegalThreadStateException.
Thread.java中的start()方法通知"线程规划器",此时线程已经准备就绪,等待调用线程对象的run()方法,这个过程就是让系统安排一个时间调用thread中的run()方法,启动线程,具有异步的效果,但是如果直接调用thread.run()方法,就不是异步的了,而是像平常那样的普通方法,而是同步,不会把线程对象交给"线程规划器"来进行处理,而是由main线程调用run方法,也就是必须等run()方法中的代码执行完成后才能执行下一步的代码.
public class Test03 {
public static void main(String[] args) {
MyThread002 myThread002 = new MyThread002(1);
MyThread002 myThread003 = new MyThread002(2);
MyThread002 myThread004 = new MyThread002(3);
MyThread002 myThread005 = new MyThread002(4);
MyThread002 myThread006 = new MyThread002(5);
MyThread002 myThread007 = new MyThread002(6);
MyThread002 myThread008 = new MyThread002(7);
MyThread002 myThread009 = new MyThread002(8);
myThread002.start();
myThread003.start();
myThread004.start();
myThread005.start();
myThread006.start();
myThread007.start();
myThread008.start();
myThread009.start();
}
}
class MyThread002 extends Thread{
private int i = 0;
public MyThread002(int i){
this.i = i;
}
@Override
public void run() {
System.out.println("创建线程 : " + this.i);
}
}
执行结果 :
所以,执行start() 的顺序并不是线程启动的顺序.
1.2.2 实现Runnable接口
使用方式:
public class Test04 {
public static void main(String[] args) {
MyThread004 myThread004 = new MyThread004();
Thread thread = new Thread(myThread004);
thread.start();
System.out.println("线程结束");
// 2. 方式2
MyThread004 myThread005 = new MyThread004();
Thread thread2 = new Thread(myThread005,"线程2");
thread2.start();
System.out.println("线程结束");
}
}
class MyThread004 implements Runnable{
@Override
public void run() {
System.out.println("通过runnable 创建线程");
}
}
1.2.3 实例变量和线程安全
线程中的实例变量分为两种:
- 不共享的数据
每个线程有字节的值,每次操作也只针对自己的数据,这种情况就是变量的不共享
public class Test05 {
public static void main(String[] args) {
MyThread005 myThread005 = new MyThread005("a");
MyThread005 myThread006 = new MyThread005("b");
MyThread005 myThread007 = new MyThread005("c");
myThread005.start();
myThread006.start();
myThread007.start();
}
}
class MyThread005 extends Thread{
private int i = 5;
public MyThread005(String name){
super();
this.setName(name);
}
@Override
public void run() {
super.run();
while (i > 0){
i--;
System.out.println("当前值是 : " + Thread.currentThread().getName() +"===" + this.i);
}
}
}
- 共享的数据
多个线程访问同一个变量
public class Test06 {
public static void main(String[] args) {
MyThread006 myThread005 = new MyThread006();
Thread thread = new Thread(myThread005,"a");
Thread thread2 = new Thread(myThread005,"b");
Thread thread3 = new Thread(myThread005,"c");
Thread thread4 = new Thread(myThread005,"e");
Thread thread5 = new Thread(myThread005,"f");
thread.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
}
}
class MyThread006 extends Thread{
private int i = 5;
@Override
public void run() {
super.run();
while (i > 0){
i--;
System.out.println("当前值是 : " + Thread.currentThread().getName() +"===" + this.i);
}
}
}
在 jvm中, i–的操作步骤是 : 获取原有的值,计算i-1,对i进行赋值
多个线程对于同一个变量进行操作,容易产生数据安全问题,可能出现错误,错误的原因是对于同一个值由两个以上的线程同时进行操作了,进而导致数据不同步,影响执行流程.所以,解决这种情况的方式是: 线程按照顺序操作共享数据中的值.
就需要用到: synchronized :
当线程执行到这个方法事,先获取锁,如果获取到锁,执行方法,如果没有获取锁,等待锁释放后再获取锁,同一时间只有一个线程获取到锁.
synchronized 可以在任意对象及方法上加锁,加锁的这段代码称之为 “互斥区” 或 “临界区”
1.2.4 留意i++ 与 System.out.println() 的异常
从上一章可以知道,解决线程安全问题的方式之一是: synchronized关键字,查看println() 源码可以知道这个方法使用了这个关键字,当i++与pirntln同时使用时,仍然有可能出现线程安全问题,原因是:虽然println方法内部是同步的,但是i++的操作确是在进入println方法之前发生的,所以仍然有一定的概率出现线程安全问题.
1.3 currentThread()方法
currentThread()方法返回代码块正在被那个线程执行的信息
class MyThread2 extends Thread {
public MyThread2() {
System.out.println("MyThread 构造器 begin");
System.out.println("当前线程状态 构造器 : " + Thread.currentThread().getState());
System.out.println("Thread.currentThread().getName() : " + Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive() : " + Thread.currentThread().isAlive());
System.out.println("this.isAlive : " + this.isAlive());
System.out.println("this.getName :" + this.getName());
System.out.println("MyThread 构造器 end");
}
@Override
public void run() {
System.out.println("-------------------------------------------------");
System.out.println("MyThread (run) begin");
System.out.println("当前线程状态 (run) : " + Thread.currentThread().getState());
System.out.println("Thread.currentThread().getName() (run) : " + Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive() (run) : " + Thread.currentThread().isAlive());
System.out.println("this.isAlive (run) : " + this.isAlive());
System.out.println("this.getName (run) :" + this.getName());
System.out.println("MyThread (run) end");
}
}
测试方法是:
/**
* 线程的存活, 指的是正在运行或者准备好运行的状态
*/
private static void aboutThreadIsAlive() throws InterruptedException {
MyThread2 myThread = new MyThread2();
Thread thread = new Thread(myThread);
System.out.println("--------------------------------------");
System.out.println("begin : " + thread.isAlive());
System.out.println("当前线程状态 : " + thread.getState());
myThread.setName("线程0");
thread.setName("线程1");
thread.start();
Thread.sleep(1000);
System.out.println("--------------------------------------");
System.out.println("end : " + thread.isAlive());
}
结果是:
从上可以看出, 线程类的构造方法是由主线程调用的,而run()方法是由新创建的线程调用的.
1.4 isAlive()方法
isAlive() 方法的功能是判断当前线程是否处于活动状态,所谓活动状态,指的是线程正处于运行或准备运行状态,已经启动并且尚未终止.
1.3中的 System.out.println(“end : " + thread.isAlive()); 打印的值是false,是因为上有个sleep()方法,如果没有这个方法,返回的有可能是true,这是因为thread的线程还没有结束,当执行sleep()后,就不是运行或者准备运行状态,所以不是"存活”,也就是false.
1.5 sleep()方法
sleep() 方法的作用是在指定毫秒内让"当前正在执行的线程"休眠(暂停执行),这个"正在执行的线程"是指this.currentThread()返回的线程.
public class Test07 {
public static void main(String[] args) {
MyThread007 myThread007 = new MyThread007();
System.out.println("begin : " + System.currentTimeMillis());
myThread007.start();
System.out.println("end : " + System.currentTimeMillis());
}
}
class MyThread007 extends Thread{
private int i = 5;
@Override
public void run() {
super.run();
for (int j = 0; j < 10; j++) {
System.out.println("打印 " + j);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
执行结果:
由于main线程与thread线程是异步的,所以先打印main线程,然后随机的时间后打印thread中的内容.
1.6 getId()方法
getId()方法获取线程的唯一id;
public class Test08 {
public static void main(String[] args) {
System.out.println("id是 : " + Thread.currentThread().getId());
}
}
结果是 :
1.7 停止线程
停止线程在java语言中并不像break语句那样干脆,需要一些技巧性的处理.
如果处理不好,将会导致超出预期的行为并且难以定位的错误.
停止线程意味着在线程处理完成之前停止正在做的操作,虽然看着简单,但是要做好防范措施,才能得到预期的结果.
停止一个线程可以使用stop().但最好不用他,虽然它确是可以停止一个正在运行的线程,但是这个方法是不安全的,而且已经被废弃了.
大多数停止线程的方法是使用Thread.interrupt()方法,虽然这个方法是"中断",但是实际上这个方法并不会终止一个正在执行的方法,还需要加入一个判断才能完成线程的终止.
终止线程的方法有三种:
- 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止,
- 使用stop()方法强行终止线程,但是不推荐,因为stop() 和 suspend() 方法和 resume()方法一样,都是过期作废的方法,使用它们可能会造成不可预知的后果.
- 使用interrupt()方法中断线程.
1.7.1 停止不了的线程
public class Test09 {
public static void main(String[] args) throws InterruptedException {
MyThread009 myThread009 = new MyThread009();
myThread009.start();
Thread.sleep(1000);
Thread.interrupted();
}
}
class MyThread009 extends Thread{
@Override
public void run() {
super.run();
for (int i = 0; i < 100; i++) {
System.out.println("i=" + (i+1));
}
}
}
结果:
运行的结果: 线程并没有结束;
1.7.2 判断线程是否是停止状态
在thread.java中提供两个api用来判断线程状态:
- this.interrupted() 测试当前线程是否已经中断, 并移除线程的中断状态
public class Test10 {
public static void main(String[] args) {
try {
MyThread10 myThread10 = new MyThread10();
myThread10.start();
// 中的线程
Thread.interrupted();
// 查看线程是否终止
System.out.println("线程是否停止1 : " + Thread.interrupted());
System.out.println("线程是否停止2 : " + Thread.interrupted());
} catch (Exception e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end");
}
}
class MyThread10 extends Thread{
@Override
public void run() {
super.run();
for (int i = 0; i < 10; i++) {
System.out.println("i=" + (i+1));
}
}
}
结果是 :
从上面结果可以看出,线程并未停止,但是自定义的线程已经停止了,所以,这个方法中的当前线程应该是调用这个方法的当前线程,在这个方法中应该是main线程.
测试主线程:
public class Test10 {
public static void main(String[] args) {
Thread.currentThread().interrupt();
// 查看线程是否终止
System.out.println("线程是否停止1 : " + Thread.interrupted());
System.out.println("线程是否停止2 : " + Thread.interrupted());
}}
```
运行结果是:

为什么第一个是true,但是第二个是false?
是因为 Thread.interrupted() 这个方法测试当前状态是否已中断,并且移除当前的中断状态,所以,第一个的时候确定了是中断状态,然后移除中断状态,第二个的时候就不是中断状态,也就是false了.
2. this.isInterrupted() 测试线程是否已经中断 ,不移除线程的中断状态
public class Test10 {
public static void main(String[] args) {
//method1();
//method2();
try {
MyThread10 myThread10 = new MyThread10();
myThread10.start();
// 中的线程
myThread10.interrupt();
// 查看线程是否终止
System.out.println("线程是否停止1 : " + myThread10.isInterrupted());
System.out.println("线程是否停止2 : " + myThread10.isInterrupted());
} catch (Exception e) {
System.out.println(“main catch”);
e.printStackTrace();
}
System.out.println(“end”);
}}
```
结果 :
可以看出两次都是true,说明 不会清除中断状态.
1.7.3 能停止的线程-异常法
在for循环中判断是否中断:
class MyThread10 extends Thread{
@Override
public void run() {
super.run();
for (int i = 0; i < 1000000; i++) {
if (this.isInterrupted()){
System.out.println("已经中断了...");
break;
}
System.out.println("i=" + (i+1));
}
System.out.println("thread run end");
}
}
public static void main(String[] args) throws InterruptedException {
try {
MyThread10 myThread10 = new MyThread10();
myThread10.start();
Thread.sleep(1000);
myThread10.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end");
}
结果 :
虽然线程停止了,但是for循环为的语句不能停止,所以,换一种能够停止所有的方法,例如: 使用异常
class MyThread10 extends Thread{
@Override
public void run() {
super.run();
try {
for (int i = 0; i < 1000000; i++) {
if (this.isInterrupted()){
System.out.println("已经中断了...");
throw new InterruptedException();
}
System.out.println("i=" + (i+1));
}
System.out.println("thread run end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
try {
MyThread10 myThread10 = new MyThread10();
myThread10.start();
Thread.sleep(1000);
myThread10.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end");
}
结果 :
从结果中可以看出,使用异常后如果抛出异常,就不会执行后面的方法.
1.7.4 在沉睡中停止
如果在sleep()状态下停止某一线程,会进入catch语句,并且清除停止状态值,使之变成false.
class MyThread10_2 extends Thread{
@Override
public void run() {
super.run();
try {
System.out.println("thread begin : ");
Thread.sleep(200000);
System.out.println("thread end : ");
} catch (InterruptedException e) {
System.out.println("thread catch...");
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
try {
MyThread10_2 myThread10 = new MyThread10_2();
myThread10.start();
Thread.sleep(1000);
myThread10.interrupt();
} catch (InterruptedException e) {
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end");
}
结果 :
1.7.5 能停止的线程-暴力停止
使用stop()方法是非常暴力的.
public class Test11 {
public static void main(String[] args) {
try {
MyThread11 myThread11 = new MyThread11();
myThread11.start();
Thread.sleep(1000);
// 使用线程终止
myThread11.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread11 extends Thread {
private int i = 0;
@Override
public void run() {
super.run();
try {
while (true) {
i++;
System.out.println("i= " + i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
System.out.println("thread catch...");
e.printStackTrace();
}
}
}
结果 :
结果显示: 使用stop()可以实现终止线程的方式,但是实际可能会出现不可预期的结果.
因为强制使用stop()使线程终止,可能会使一些清理性的工作得不到完成,另外,就是对锁定的对象进行了"解锁".导致数据得不到同步处理,出现数据不一致的情况.
1.7.6 方法stop()与java.lang.ThreadDeath异常
调用stop() 方法时会抛出java.lang.ThreadDeath异常,但是通常情况下,这个异常不需要显示的捕捉,.
public class Test12 {
public static void main(String[] args) {
MyThread12 myThread12 = new MyThread12();
myThread12.start();
}
}
class MyThread12 extends Thread{
@Override
public void run() {
super.run();
try {
System.out.println("begin ... ");
this.stop();
System.out.println("end ... ");
} catch (ThreadDeath e) {
System.out.println("thread catch ...");
e.printStackTrace();
}
}
}
结果:
注意: 如果抛出异常是exception,则不会显示上述异常:
结果:
1.7.7 释放锁的不良后果
使用stop()方法释放锁将会给数据造成不一致的结果.如果出现这种情况,程序处理的数据就与可能造成破坏,最终导致程序执行的流程错误,一定要特别注意.
public class Test13 {
public static void main(String[] args) {
try {
TestUser testUser = new TestUser();
MyThread13 myThread13 = new MyThread13(testUser);
myThread13.start();
Thread.sleep(50);
// 线程终止
myThread13.stop();
System.out.println(testUser.getUsername() + "----" + testUser.getPassword());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread13 extends Thread {
private TestUser testUser;
public MyThread13(TestUser testUser) {
super();
this.testUser = testUser;
}
@Override
public void run() {
super.run();
testUser.printUser("cc", "bb");
}
}
class TestUser {
private String username = "a";
private String password = "b";
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public synchronized void printUser(String username, String password) {
try {
this.username = username;
Thread.sleep(1000);
this.password = password;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
结果 :
从上可以看出,结果与期望值有区别,是由于数据未同步造成的.在线程调用stop()方法的时候,可能同步方法内的数据还没有执行完,所以数据会有差距.
1.7.8 使用return停止线程
将interrupt() 与 return结合使用也能实现停止线程的效果.
public class Test14 {
public static void main(String[] args) {
try {
MyThread14 myThread14 = new MyThread14();
myThread14.start();
Thread.sleep(2000);
// 线程中断
myThread14.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread14 extends Thread {
private int i = 0;
@Override
public void run() {
super.run();
while (true) {
System.out.println("i = " + (i++));
if (this.isInterrupted()) {
System.out.println("线程停止了 ... ");
return;
}
}
}
}
结果 :
从结果可以看出,达到了结束线程的结果
1.8 暂停线程
暂停线程意味着此线程还可以恢复运行,在java多线程中,可以使用suspend()方法暂时停止线程,使用resume()方法恢复线程.
1.8.1 suspend() 与 resume() 方法的使用
public class Test15 {
public static void main(String[] args) {
try {
MyThread15 myThread15 = new MyThread15();
myThread15.start();
Thread.sleep(1000);
// a 段
myThread15.suspend();
System.out.println("a = " + System.currentTimeMillis() + " id = " + myThread15.getI());
Thread.sleep(5000);
System.out.println("a = " + System.currentTimeMillis() + " id = " + myThread15.getI());
// b 段
myThread15.resume();
System.out.println("b = " + System.currentTimeMillis() + " id = " + myThread15.getI());
Thread.sleep(5000);
System.out.println("b = " + System.currentTimeMillis() + " id = " + myThread15.getI());
// c 段
myThread15.suspend();
System.out.println("c = " + System.currentTimeMillis() + " id = " + myThread15.getI());
Thread.sleep(5000);
System.out.println("c = " + System.currentTimeMillis() + " id = " + myThread15.getI());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread15 extends Thread {
private long i = 0;
public long getI() {
return i;
}
public void setI(long i) {
this.i = i;
}
@Override
public void run() {
super.run();
while (true) {
i++;
}
}
}
结果 :
从结果看,线程确是被暂停了,也被恢复了
1.8.2 suspend() 与 resume() 方法的缺点-独占
在使用suspend()与resume()方法时,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象.
public class Test16 {
public static void main(String[] args) {
try {
final Synchr synchr = new Synchr();
Thread thread = new Thread(() -> {
synchr.print();
});
thread.setName("a");
thread.start();
Thread.sleep(1000);
Thread thread1 = new Thread(() -> {
System.out.println("线程2启动了 ... 但进入不了 synchr中 ...");
System.out.println("因为print方法已经被a线程锁定了,并且永远suspend 暂停了 ...");
synchr.print();
});
thread1.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Synchr {
public synchronized void print() {
System.out.println("begin ... ");
if (Thread.currentThread().getName().equals("a")) {
System.out.println("线程a 永远 suspend 了");
// 暂停线程
Thread.currentThread().suspend();
}
}
}
结果 :
另一种方式独占锁的情况也需要格外注意,稍有不慎,就会掉进"坑"里.
public class Test17 {
public static void main(String[] args) {
try {
MyThread17 myThread17 = new MyThread17();
myThread17.start();
Thread.sleep(1000);
// 暂停线程
myThread17.suspend();
System.out.println("main end ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread17 extends Thread {
private long i = 0;
@Override
public void run() {
super.run();
while (true) {
i++;
}
}
}
结果 :
使用print方法打印结果:
public class Test17 {
public static void main(String[] args) {
try {
MyThread17 myThread17 = new MyThread17();
myThread17.start();
Thread.sleep(1000);
// 暂停线程
myThread17.suspend();
System.out.println("main end ");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyThread17 extends Thread {
private long i = 0;
@Override
public void run() {
super.run();
while (true) {
i++;
System.out.println(i);
}
}
}
结果 :
虽然print方法内部使用synchronized,当程序运行到println()方法内部停止时,同步锁未被释放;
这导致当前printStream对象的println()方法一直呈"暂停"状态,并且"锁未释放",而main()方法中的代码System.out.println(“main end”); 迟迟不能执行打印.
1.8.3 suspend() 与 resume() 方法的缺点-不同步
使用suspend() 与 resume() 方法时也容易出现因为线程的暂停而导致数据的不同步的情况.
public class Test18 {
public static void main(String[] args) {
try {
MyObject myObject = new MyObject();
Thread thread = new Thread(() -> {
myObject.setValue("22", "22");
});
thread.setName("a");
thread.start();
Thread.sleep(1000);
Thread thread1 = new Thread(()->{
myObject.printUsernamePassword();
});
thread1.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class MyObject {
private String username = "1";
private String password = "11";
public void setValue(String username, String password) {
this.username = username;
if (Thread.currentThread().getName().equals("a")) {
System.out.println("停止a线程 .. ");
Thread.currentThread().suspend();
}
this.password = password;
}
public void printUsernamePassword() {
System.out.println(username + "---" + password);
}
}
结果 :
所以, 使用suspend() 方法要格外注意.
1.9 yield()方法
yieId()方法的作用是放弃当前cpu资源,将它让给其他的任务去占用cpu执行时间,当放弃的时间不确定,有可能刚刚放弃,马上又获得cpu时间片.
public class Test19 {
public static void main(String[] args) {
MyThread19 myThread19 = new MyThread19();
myThread19.start();
}
}
class MyThread19 extends Thread{
@Override
public void run() {
super.run();
long l = System.currentTimeMillis();
int count = 0;
for (int i = 0; i < 50000000; i++) {
Thread.yield();
count = count + (i + 1);
}
System.out.println("费时 : " + (System.currentTimeMillis() - l));
}
}
结果 :
1.10 线程的优先级
在操作系统中,线程可以优先划分优先级,优先级较高额线程得到的cpu资源较多,也就是cpu优先执行优先级较高的线程对象中的任务.
设置线程优先级有助于帮助"线程规划器"确定下一次选择那个线程来优先执行,
设置线程的优先级使用setPriority(),源码如下:
在java中,线程的优先级分为1~10这10个等级,如果小于1或者大于10,则jdk抛出异常throw new IllegalArgumentException();
JDK中线程常用的等级:
1.10.1 线程优先级的继承特性
在JAVA中,线程的优先级具有继承性,比如,a线程启动b线程,则b线程的优先级与a是一样的.
public class Test20 {
public static void main(String[] args) {
System.out.println("main 优先级 start : " + Thread.currentThread().getPriority());
Thread.currentThread().setPriority(6);
System.out.println("main 优先级 end : " + Thread.currentThread().getPriority());
MyThread20 myThread20 = new MyThread20();
myThread20.start();
}
}
class MyThread20 extends Thread{
@Override
public void run() {
super.run();
System.out.println("MyThread20 线程的优先级是 : " + this.getPriority());
MyThread20_1 myThread20_1 = new MyThread20_1();
myThread20_1.start();
}
}
class MyThread20_1 extends Thread{
@Override
public void run() {
super.run();
System.out.println("MyThread20_1 线程的优先级是 : " + this.getPriority());
}
}
结果 :
由图中的结果可以看出,线程的优先级具有继承的特性.
1.10.2 优先级具有规则性
优先级高的线程总是大部分先执行,但不代表高优先级的线程全部先执行完.
当线程优先级的等级差距很大时,谁先执行完和代码的调用顺序无关.
cpu尽量将执行资源让给优先级比较高的线程.
public class Test21 {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
MyThread21_1 myThread21_1 = new MyThread21_1();
myThread21_1.setPriority(1);
myThread21_1.start();
MyThread21_2 myThread21_2 = new MyThread21_2();
myThread21_2.setPriority(10);
myThread21_2.start();
}
}
}
class MyThread21_1 extends Thread{
@Override
public void run() {
super.run();
long l = System.currentTimeMillis();
long addresult = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 5000; j++) {
Random random = new Random();
random.nextInt();
addresult = addresult + 1;
}
}
System.out.println("★ ★ ★ ★ ★ 当前线程用时 : " + (System.currentTimeMillis() - l));
}
}
class MyThread21_2 extends Thread{
@Override
public void run() {
super.run();
long l = System.currentTimeMillis();
long addresult = 0;
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 5000; j++) {
Random random = new Random();
random.nextInt();
addresult = addresult + 1;
}
}
System.out.println("☆ ☆ ☆ ☆ ☆ 当前线程用时 : " + (System.currentTimeMillis() - l));
}
}
结果 :
public class Test21 {
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
MyThread21_1 myThread21_1 = new MyThread21_1();
myThread21_1.setPriority(10);
myThread21_1.start();
MyThread21_2 myThread21_2 = new MyThread21_2();
myThread21_2.setPriority(1);
myThread21_2.start();
}
}
}
结果 :
1.10.3 优先级具有随机性
因为线程的优先级还具有"随机性",也就是优先级较高的线程不一定每一次都执行完.
线程的优先级设置相近
结论:
不要把线程的优先级与运行结果的顺序作为衡量的标准,优先级高的线程不一定先执行完run()方法中的任务.
1.10.4 看谁运行的快
public class Test22 {
public static void main(String[] args) {
try {
MyThread22_1 myThread22_1 = new MyThread22_1();
myThread22_1.setPriority(Thread.NORM_PRIORITY - 3);
myThread22_1.start();
MyThread22_2 myThread22_2 = new MyThread22_2();
myThread22_2.setPriority(Thread.NORM_PRIORITY + 3);
myThread22_2.start();
// 当前线程是 主 线程的
Thread.sleep(1000);
myThread22_1.stop();
myThread22_2.stop();
System.out.println("线程1 : " + myThread22_1.getCount());
System.out.println("线程2 : " + myThread22_2.getCount());
} catch (Exception e) {
e.printStackTrace();
}
}
}
class MyThread22_1 extends Thread{
private int count = 0;
public int getCount() {
return count;
}
@Override
public void run() {
super.run();
while (true){
count++;
}
}
}
class MyThread22_2 extends Thread{
private int count = 0;
public int getCount() {
return count;
}
@Override
public void run() {
super.run();
while (true){
count++;
}
}
}
结果 :
1.11 守护线程
java 线程中有两种线程,一种是用户线程,另一种是守护线程
守护线程是一种特殊的线程,他的特性是 “陪伴”,当进程中不存在非守护线程了,那守护线程就自动销毁(随着jvm一同结束工作).
典型的守护线程就是垃圾回收线程
class MyThread4 extends Thread{
private int i = 0;
@Override
public void run() {
while (true){
i++;
System.out.println("当前i的值是 : " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 关于守护线程
* java 中线程有两种, 一种是用户线程,一种是守护线程
* 我们通过new thread().start() 的线程是用户线程
* 守护线程 是通过用户线程的 setDaemon()方法设置的,
* 守护线程可以理解为未用户线程服务的,当jvm中没有非守护线程后,守护线程会随着jvm一同结束工作
*/
private static void aboutThreadDaemon() throws InterruptedException {
MyThread4 myThread4 = new MyThread4();
// 设置为守护线程
myThread4.setDaemon(true);
myThread4.start();
System.out.println("当前线程 : " + Thread.currentThread().getName());
System.out.println("当前线程状态 : " + Thread.currentThread().getState());
System.out.println("当前线程是否存活 : " + Thread.currentThread().isAlive());
Thread.sleep(2000);
System.out.println("当前线程 : " + Thread.currentThread().getName());
System.out.println("当前线程状态 : " + Thread.currentThread().getState());
System.out.println("当前线程是否存活 : " + Thread.currentThread().isAlive());
System.out.println("当前线程是否存活 : " + Thread.currentThread().isAlive());
System.out.println("我离开,myThread4对象也不在打印了,结束了");
}
结果 :
1.12 本章小结
本章介绍了thread类的api,在使用这些api’的过程中,会出现一些意想不到的情况,其实也就是多线程具有不可预知性的一个体现,学习掌握这些常用情况,也就掌握了多线程开发的命脉与习性,是学习多线程更深层知识的基础.