目录直通车
一、 进入正题之前,先看“三连”
什么是程序(Progress)?
使用某编程语言编写的,为了完成特定任务的静态的代码。
什么是进程(Program)?
程序的一次执行过程,或者正在运行的一个程序(比如说正在运行的微信和网易云音乐)。动态过程:有它自身的产生、存在、消亡的过程。
什么是线程(Thread)?
说白了,进程可以细分线程,所以进程一般是由很多线程组成的,是程序内部的一条执行路径。多线程就是在同一时间执行多个线程(同一个程序里面需要同时执行多个任务,比如说360可以同时体验和清理垃圾)。
二、 线程例子
在子线程里完成1-100的输出,主线程也执行相同操作。
public class TestThread {
/**
* 在子线程里完成1-100的输出,主线程也执行相同操作。
*/
public static void main(String[] args) {
SubThread st =new SubThread();
/*
* public void start()导致此线程开始执行; Java虚拟机调用此线程的run方法。
* 结果是两个线程同时运行:当前线程(从调用返回到start方法)和另一个线程(执行其run方法)。
* 不止一次启动线程是不合法的。 特别地,一旦线程 完成执行就可能不会重新启动。
*/
st.start();
for (int i = 1;i<101;i++){
// Thread.currentThread().getName() 获取当前线程的名称
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
class SubThread extends Thread{
/**
* 重写Thread类run()方法,方法内完成子线程完成的事情
*/
public void run(){
for (int i = 1;i<101;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
写到 st.start() 的时候,发现点出来的还有个 run() 的方法。问题:为什么使用的start来启动线程而不使用run,那么run与start有什么区别呢?接下来将start换成run启动一下,观察运行结果:
结果说明,子线程没有启动。
结论:不能通过Thread实现类对象的run()方法去启动一个线程。
三、 线程的创建与使用
创建线程SubThread st2 =new SubThread();
再次运行st2.start() 即使用。
public class TestThread {
/**
* 在子线程里完成1-100的输出,主线程也执行相同操作。
*/
public static void main(String[] args) {
SubThread st =new SubThread();
SubThread st2 =new SubThread();
/*
* public void start()导致此线程开始执行; Java虚拟机调用此线程的run方法。
* 结果是两个线程同时运行:当前线程(从调用返回到start方法)和另一个线程(执行其run方法)。
* 不止一次启动线程是不合法的。 特别地,一旦线程 完成执行就可能不会重新启动。
*/
st.start();
st2.start();
for (int i = 1;i<101;i++){
// Thread.currentThread().getName() 获取当前线程的名称
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
class SubThread extends Thread{
/**
* 重写Thread类run()方法,方法内完成子线程完成的事情
*/
public void run(){
for (int i = 1;i<101;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
四、 Thread方法总结
上述例子中有几个方法,已经或多或少都有了一定的理解。
1、 start()
启动线程并执行相应的run()方法
2、 run()
在使用类的继承之后,run方法是一定要重写的,所以在run里面运行子线程需要被 执行的代码。
3、 currentThread()
静态的,调用当前线程。
4、 getName()
获取当前线程的名字。
5、 setName()
设置当前线程的名字。
6、 yield()
调用子方法的线程释放当前CPU的执行权(举个例子,当前有一根棒棒糖,现在有子线程和主线程两个人去抢,谁抢到就是谁的),yield的用途:比如说一个线程执行到等待状态的时候,此时让其它的线程拿到CPU执行权。那么是否可以设置优先级呢?
下面实例,多运行几次,观察结果:
public class TestThread {
/**
* 在子线程里完成1-100的输出,主线程也执行相同操作。
*/
public static void main(String[] args) {
// 创建线程
SubThread st =new SubThread();
/*
* public void start()导致此线程开始执行; Java虚拟机调用此线程的run方法。
* 结果是两个线程同时运行:当前线程(从调用返回到start方法)和另一个线程(执行其run方法)。
* 一个线程只能启动一次。 特别地,一旦线程 完成执行就可能不会重新启动。
*/
st.setName("子线程:");
Thread.currentThread().setName("主线程:");
st.start();
for (int i = 1;i<101;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
if (i % 10 == 0){
Thread.currentThread().yield();
}
}
}
}
class SubThread extends Thread{
/**
* 重写Thread类run()方法,方法内完成子线程完成的事情
*/
public void run(){
for (int i = 1;i<101;i++){
// Thread.currentThread().getName() 获取当前线程的名称
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
7、 Join()
比如,在A线程中调用B线程中的join方法,则A停止执行,优先B执行,B执行完了以后在接着join()之后代码执行。类似于学生在排队打饭的时候,学生让老师来插队,老师打完饭之后,后面的人再接着打饭。
public class TestThread {
/**
* 在子线程里完成1-100的输出,主线程也执行相同操作。
*/
public static void main(String[] args) {
// 创建线程
SubThread st =new SubThread();
/*
* public void start()导致此线程开始执行; Java虚拟机调用此线程的run方法。
* 结果是两个线程同时运行:当前线程(从调用返回到start方法)和另一个线程(执行其run方法)。
* 一个线程只能启动一次。 特别地,一旦线程 完成执行就可能不会重新启动。
*/
st.setName("子线程:");
Thread.currentThread().setName("主线程:");
st.start();
for (int i = 1;i<101;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
if (i == 20){
try{
st.join();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
}
class SubThread extends Thread{
/**
* 重写Thread类run()方法,方法内完成子线程完成的事情
*/
public void run(){
for (int i = 1;i<101;i++){
// Thread.currentThread().getName() 获取当前线程的名称
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
运行效果:
8、 isAlive()
判断当前线程是否还存活。
9、 Sleep(long x)
显示的让当前线程睡眠x毫秒。
五、 线程的优先级
MAX_PRIORITY(10) 最大为10
MIN_PRIORITY(1) 最小为1
NORM_PRIORITY(5) 默认为5
方法:
getPriority():返回线程优先级。
setPriority(int newPriority) :改变线程的优先级。
线程创建时会继承父线程的优先级。
注意:优先级只增大抢到CPU执行权的概率,并不存在先执行和后执行的问题,优先级越大,抢到的概率越大。
例子:
public class TestThread {
public static void main(String[] args) {
// 创建线程
SubThread st =new SubThread();
/*
* public void start()导致此线程开始执行; Java虚拟机调用此线程的run方法。
* 结果是两个线程同时运行:当前线程(从调用返回到start方法)和另一个线程(执行其run方法)。
* 一个线程只能启动一次。 特别地,一旦线程 完成执行就可能不会重新启动。
*/
st.setName("子线程");
/*
设置线程的优先级
设置为最大的优先级时,可以为10,也可以为Thread.MAX_PRIORITY
*/
st.setPriority(Thread.MAX_PRIORITY);
Thread.currentThread().setName("主线程");
st.start();
System.out.println(st.getName()+"的优先级为:"+st.getPriority());
System.out.println(Thread.currentThread().getName()+"的优先级为:"+Thread.currentThread().getPriority());
for (int i = 1;i<101;i++){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
class SubThread extends Thread{
/**
* 重写Thread类run()方法,方法内完成子线程完成的事情
*/
public void run(){
for (int i = 1;i<101;i++){
// Thread.currentThread().getName() 获取当前线程的名称
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
六、 线程实例
创建两个子线程,分别输出0-100的奇数和偶数。
首先有两种方式实现:匿名和非匿名两种,匿名方式的代码更加简洁。
1、 非匿名方式
public class TestThread {
public static void main(String[] args) {
SubThread1 st1 = new SubThread1();
SubThread2 st2 = new SubThread2();
st1.start();
st2.start();
}
}
class SubThread1 extends Thread{
public void run(){
for (int i=1;i<101;i++){
if (i % 2 == 0){
System.out.println(i);
}
}
}
}
class SubThread2 extends Thread{
public void run(){
for (int i=1;i<101;i++){
if (i % 2 != 0){
System.out.println(i);
}
}
}
}
2、 匿名方式
public class TestThread {
public static void main(String[] args) {
new Thread(()->{
for (int i=1;i<101;i++){
if (i % 2 == 0){
System.out.println(i);
}
}
}).start();
new Thread(()->{
for (int i=1;i<101;i++){
if (i % 2 != 0){
System.out.println(i);
}
}
}).start();
}
}