一、多线程三种实现方式的对比
运行结果 |
优点 |
缺点 |
|
继承Thread类 |
无法获取 |
编程比较简单,可以直接使用 |
扩展性较差,不能再继承其他的类 |
实现Runnable接口 |
扩展性强,实现该接口的同时还可以继承其他的类 |
编程相对复杂,不能直接使用Thread类中的方法 |
|
实现Callable接口 |
可以获取 |
二、Thread类常见成员方法
方法名称 |
说明 |
String getName() |
返回此线程的名称 |
void setName(String name) |
设置线程的名字(构造方法也可以设置名字) |
static Thread currentThread() |
获取当前线程的对象 |
static void sleep(long time) |
让线程休眠指定的时间,单位为毫秒 |
setPriority(int newPriority) |
设置线程的优先级 |
final int getpriority() |
获取线程的优先级 |
final void setDaemon(boolean on) |
设置为守护线程 |
public static void yield() |
出让线程/礼让线程 |
public static void join() |
插入线程/插队线程 |
(一)getName()和setName()
可以通过setName方法手动给线程命名,否则线程将使用默认名称,格式为Thread-X,其中X为线程的序号,从0开始编号。另外,也可以通过Thread类的构造方法public Thread(String name)给线程命名。
(一)static currentThread()
获取当前线程对象。当JVM虚拟机启动之后,会自动的启动多条线程,其中有一条线程就叫做main线程。他的作用就是去调用main方法,并执行里面的代码。在以前,我们写的所有的代码,其实都是运行在main线程当中。
注意这是一个静态方法,通过类名调用,并不是通过Thread对象调用。因此返回的Thread对象是当前运行的线程的对象。
(二)static sleep()休眠
使当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,但受系统计时器和调度器的精度和准确性的限制。该线程不会失去任何监视器的所有权。
注意这是一个静态方法,直接通过Thread调用,表示将当前的线程睡眠一定的时间。
线程类实现代码:
public class MyThread extends Thread{
public MyThread(String name){
super(name);
}
public MyThread(){
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
// 休眠1秒钟
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 要执行的代码
System.out.println(getName() + (i + 1));
}
}
}
线程使用:
public class ThreadDemo4 {
public static void main(String[] args) throws InterruptedException {
/*
String getName() 返回此线程的名称
void setName(String name) 设置线程的名字(构造方法也可以设置名
细节:
1、如果我们没有给线程设置名字,线程也是有默认的名字的
格式:Thread-X(X序号,从0开始的)
2、如果我们要给线程设置名字,可以用set方法进行设置,也可以构造方法设置
static Thread currentThread() 获取当前线程的对象
static void sleep(long time) 让线程休眠指定的时间,单位为毫秒
*/
// 1。创建线程的对象
MyThread mt1 = new MyThread("飞机");
MyThread mt2 = new MyThread("大炮");
// 设置优先级
mt1.setPriority(10);
mt2.setPriority(1);
// 2。开启线程
mt1.start();
mt2.start();
/*
// 3.获取线程
Thread thread = Thread.currentThread();
String name = thread.getName();
System.out.println(name);
*/
/*
// 线程睡眠
System.out.println("111111111");
long startTime = System.currentTimeMillis();
Thread.sleep(3000);
System.out.println("222222222");
System.out.println((System.currentTimeMillis() - startTime));
*/
}
}
(三)setPriority和getPriority
更改线程优先级,最小是1,最大是10,优先级越大越有可能抢占到CPU时间,默认是5。
线程的调度,一般有2种:
l 抢占式调度
l 非抢占式调度
Java中采用抢占式调度的方法,随机性高,线程的优先级越高就越有可能抢到CPU执行时间。因此,优先级高的线程只是获得CPU时间的可能性更高,但是也有可能低优先级的线程排在高优先级的线程前执行。
(二)setDaemon(boolean on)
将此线程标记为守护线程或用户线程。当运行的线程都是守护线程时,Java虚拟机退出。必须在启动线程之前调用此方法。
当其他的非守护线程执行完毕之后,守护线程会陆续结束,这意味着守护线程不一定会按照代码指定的方式执行结束,很有可能守护线程执行到中途就会被退出。
public class ThreadDemo5 {
public static void main(String[] args) {
/*
final void setDaemon(boolean on) 设置为守护线程
细节:
当其他的非守护线程执行完毕之后,守护线程会陆续结束
通俗易懂的理解:
当女神线程结束了,那么备胎也没有存在的必要了
*/
// 创建线程对象
MyThread01 t1 = new MyThread01(5);
MyThread01 t2 = new MyThread01(20);
// 命名线程
t1.setName("被偏爱的");
t2.setName("备胎");
// 把第二个线程设置为守护线程
t2.setDaemon(true);
// 开启线程
t1.start();
t2.start();
}
}
MyThread01类的代码:
public class MyThread01 extends Thread{
private int count = 0;
public MyThread01(int count){
this.count = count;
}
public MyThread01(){
this.count = 100;
}
@java.lang.Override
public void run() {
// 线程要执行的代码
for (int i = 0; i < count; i++) {
System.out.println(getName() + ": hello world." + i);
}
}
}
以上代码中,t1线程只需要执行5次即完成,而t2守护线程需要执行20次,当t1执行完毕后,t2作为守护线程会陆续退出,一般执行不到20次即会退出。
一次运行的结果:
备胎: hello world.0
备胎: hello world.1
备胎: hello world.2
被偏爱的: hello world.0
备胎: hello world.3
被偏爱的: hello world.1
备胎: hello world.4
被偏爱的: hello world.2
备胎: hello world.5
被偏爱的: hello world.3
备胎: hello world.6
被偏爱的: hello world.4
备胎: hello world.7
备胎: hello world.8
备胎: hello world.9
备胎线程运行了9次即退出了。
(三)yield方法
yield方法表示交出当前线程的执行权。
public class MyThread extends Thread{
public MyThread(String name){
super(name);
}
public MyThread(){
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
/*
try {
// 休眠1秒钟
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
*/
// 要执行的代码
System.out.println(getName() + (i + 1));
// 表示出让cpu的执行权
Thread.yield();
}
}
}
执行代码:
public class ThreadDemo6 {
public static void main(String[] args) {
MyThread mt1 = new MyThread("飞机");
MyThread mt2 = new MyThread("大炮");
// 执行
mt1.start();
mt2.start();
}
}
以上代码在运行时会更加均匀的分次执行。
(四)join方法
join方法表示把当前线程插入到某个线程之前进行执行。
public class MyThreadJoin extends Thread{
public MyThreadJoin(String name){
super(name);
}
public MyThreadJoin(){
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
// 要执行的代码
System.out.println(getName() + "@" + (i + 1));
}
}
}
执行代码:
public class MyThreadJoinDemo {
public static void main(String[] args) throws InterruptedException {
/*
public final void join() 插入线程/插队线程
*/
MyThreadJoin mtj = new MyThreadJoin();
mtj.setName("土豆");
mtj.start();
// 表示把mtj这个线程,插入到当前线程之前
// mtj:土豆线程
// 当前线程:即是main线程
mtj.join();
// 在main线程中执行
for (int i = 0; i < 10; i++) {
System.out.println("main线程@" + i);
}
}
}
一、线程的生命周期
问:sleep方法会让线程睡眠,睡眠时间到了之后,立马就会执行下面的代码吗?
答:不一定,睡眠结束后线程进入就绪状态,需要抢夺到执行权之后才会继续执行。