目录
4.2声明一个匿名内部类继承Thread类,重写run方法,实现打印[1,100]之间的偶数,要求每隔1秒打印1个偶数。
1.什么是程序、进程与线程
- 程序(program):为完成特定任务,用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
-
进程(process):程序的一次执行过程,或是正在内存中运行的应用程序。如:运行中的QQ,运行中的网易音乐播放器。
-
线程(thread):进程可进一步细化为线程,是程序内部的一条执行路径。一个进程中至少有一个线程。
2.创建并启动线程
2.1继承Thread类方式
public class MyThread extends Thread{
public MyThread(String name){
super(name);//设置线程名
}
/*
* 重写run方法
* 创建线程
* */
@Override
public void run() {
System.out.println(getName()+"正在执行!");
}
}
运行线程
public class TestMyThread {
public static void main(String[] args) {
MyThread mt1 = new MyThread("子线程1");
mt1.start();//start运行线程
MyThread mt2 = new MyThread("子线程2");
mt2.start();
MyThread mt3 = new MyThread("子线程3");
mt3.start();
}
}
2.2实现Runnable接口方式
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "正在运行!");
}
}
运行线程
public class TestMyRunnable {
public static void main(String[] args) {
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr,"子线程1");
t1.start();
Thread t2 = new Thread(mr,"子线程2");
t2.start();
Thread t3 = new Thread(mr,"子线程3");
t3.start();
}
}
3运行结果
4.二者方式的区别
- 当一个类扩展 Thread 类时,您不能扩展任何其他类,但通过实现 Runnable 接口,也可以从另一个类扩展,例如: class
MyClass extends OtherClass implements Runnable
. -
继承Thread:线程代码存放Thread子类run方法中。
-
实现Runnable:线程代码存在接口的子类的run方法。
-
实现Runnable不返回结果,也不能抛出已检查的异常。
5.二者哪种方式较好
实现Runnable接口比继承Thread类所具有的优势
- 避免了单继承的局限性
- 多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
Runnable
接口非常适合于需要在多个线程之间共享任务逻辑的场合,以及那些简单且可重复使用的任务。
6.第三种方式:Callable接口方式(JDK5.0新增)
点击查看(Callable详解)https://blog.csdn.net/m0_61160520/article/details/131636644?spm=1001.2014.3001.5501
3. Thread类的常用结构
3.1 构造器
public Thread() | 分配一个新的线程对象。 |
public Thread(String name) | 分配一个指定名字的新的线程对象。 |
public Thread(Runnable target) | 指定创建线程的目标对象,它实现了Runnable接口中的run方法 |
public Thread(Runnable target,String name) | 分配一个带有指定目标新的线程对象并指定名字。 |
3.2 常用方法系列1
public void run() | 此线程要执行的任务在此处定义代码。 |
public void start() | 导致此线程开始执行; Java虚拟机调用此线程的run方法。 |
public String getName() | 获取当前线程名称。 |
public void setName(String name) | 设置该线程名称。 |
public static Thread currentThread() | 返回对当前正在执行的线程对象的引用。在Thread子类中就是this,通常用于主线程和Runnable实现类 |
public static void sleep(long millis) | 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行)。 |
public static void yield() | yield只是让当前线程暂停一下,让系统的线程调度器重新调度一次,希望优先级与当前线程相同或更高的其他线程能够获得执行机会,但是这个不能保证,完全有可能的情况是,当某个线程调用了yield方法暂停之后,线程调度器又将其调度出来重新执行。 |
3.3 常用方法系列2
public final boolean isAlive() | 测试线程是否处于活动状态。如果线程已经启动且尚未终止,则为活动状态。 |
void join() | 等待该线程终止。 |
void join(long millis) | 等待该线程终止的时间最长为 millis 毫秒。如果millis时间到,将不再等待。 |
void join(long millis, int nanos) | 等待该线程终止的时间最长为 millis 毫秒 + nanos 纳秒。 |
3.4 常用方法系列3
每个线程都有一定的优先级,同优先级线程组成先进先出队列(先到先服务),使用分时调度策略。优先级高的线程采用抢占式策略,获得较多的执行机会。每个线程默认的优先级都与创建它的父线程具有相同的优先级。
-
Thread类的三个优先级常量
-
public final int getPriority() 返回线程优先级 public final void setPriority(int newPriority) 改变线程的优先级,范围在[1,10]之间
MAX_PRIORITY(10) | 最高优先级 |
MIN _PRIORITY (1) | 最低优先级 |
NORM_PRIORITY (5) | 普通优先级,默认情况下main线程具有普通优先级。 |
4.例题
4.1获取main线程和其他线程的名称和优先级。
声明一个匿名内部类继承Thread类,重写run方法,在run方法中获取线程名称和优先级。设置该线程优先级为最高优先级并启动该线程。
/*
* 获取main线程对象和其他线程的名称和优先级。
* 声明一个匿名内部类继承Thread类,重写run方法,在run方法中获取线程名称和优先级。
* 设置该线程优先级为最高优先级并启动该线程。
* 再次获取优先级
* */
public class Eg1 extends Thread{
public static void main(String[] args) {
Thread t1 = new Thread("线程1"){
/*创建线程*/
@Override
public void run() {
super.run();
//输出默认优先级
System.out.println(getName()+"的优先级为:"+getPriority());
}
};
t1.setPriority(Thread.MIN_PRIORITY);//设置为最高优先级
t1.start();//再运行
System.out.println(t1.getName()+"的优先级为:"+t1.getPriority());
System.out.println(Thread.currentThread().getName()+"的优先级为:"+Thread.
currentThread().getPriority());//main的优先级
}
}
输出:
线程1的优先级为:1
main的优先级为:5
线程1的优先级为:1
4.2声明一个匿名内部类继承Thread类,重写run方法,实现打印[1,100]之间的偶数,要求每隔1秒打印1个偶数。
public class Eg2 {
public static void main(String[] args) {
Thread te = new Thread(() -> {
for (int i = 2; i <= 100; i += 2) {
System.out.println("偶数:" + i);
try {
Thread.sleep(1000);//使当前正在执行的线程暂停 1 millis(暂时停止执行)。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
te.start();
}
}
4.3线程停止与相互调度
- 声明一个匿名内部类继承Thread类,重写run方法,实现打印[1,100]之间的偶数,要求每隔1秒打印1个偶数。
- 声明一个匿名内部类继承Thread类,重写run方法,实现打印[1,100]之间的奇数,当打印到5时,让奇数线程暂停一下,再继续。当打印到5时,让奇数线程停下来,让偶数线程执行完再打印。
public class Eg3 {
public static void main(String[] args) {
Thread te = new Thread(() -> {
for (int i = 2; i <= 20; i += 2) {
System.out.println("偶数线程:" + i);
try {
Thread.sleep(1000);//可以让程序放慢运行,然后让甲方加钱再优化(滑稽)
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
te.setPriority(Thread.MAX_PRIORITY);
te.start();
Thread to = new Thread(() -> {
for (int i = 1; i <= 20; i += 2) {
System.out.println("奇数线程:" + i);
if (i == 5) {
Thread.yield();
/*yield让当前线程暂停一下,让系统的线程调度器重新调度一次,
*希望优先级与当前线程相同或更高的其他线程能够获得执行机会,
*但是这个不能保证,完全有可能的情况是,当某个线程调用了yield方法暂停之后,
*线程调度器又将其调度出来重新执行。*/
try {
te.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
to.start();
}
}
输出:
偶数线程:2
奇数线程:1
奇数线程:3
奇数线程:5
偶数线程:4
偶数线程:6
偶数线程:8
偶数线程:10
偶数线程:12
偶数线程:14
偶数线程:16
偶数线程:18
偶数线程:20
奇数线程:7
奇数线程:9
奇数线程:11
奇数线程:13
奇数线程:15
奇数线程:17
奇数线程:19
5.守护线程
5.1守护线程特点
有一种线程,它是在后台运行的,它的任务是为其他线程提供服务的,这种线程被称为“守护线程”。JVM的垃圾回收线程就是典型的守护线程。
守护线程有个特点,就是如果所有非守护线程都死亡,那么守护线程自动死亡。
public class Daemon extends Thread{
public static void main(String[] args) {
System.out.println("main开始进行");
Thread t1 = new Thread("守护线程"){
//创建线程
@Override
public void run() {
super.run();
for (int i = 1; i <= 10;i++) {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"正在运行第:"+i+"次");
} catch (InterruptedException e) {
System.out.println("休眠异常!");
}
}
}
};
Thread t2 = new Thread("普通线程"){
//创建线程
@Override
public void run() {
super.run();
for (int i = 1; i <= 5;i++) {
try {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"正在运行第:"+i+"次");
} catch (InterruptedException e) {
System.out.println("休眠异常!");
}
}
}
};
t1.setDaemon(true);
t2.setDaemon(false);
t1.start();
t2.start();
System.out.println("main结束进行");
}
}
运行结果:
5.2小结论
- 主线程,main执行结束后,普通线程可以继续执行直至执行完毕;
- 用户线程执行完毕后,守护线程立刻结束;