了解如何使用Thread 类实现多线程之后,继续学习Thread 类实现多线程之后的相关功能及方法。其中的一些方法是 static 的,由类名直接调用,通常都是在那个线程中执行,这些静态的方法就指定的那个线程。
Demo 代码示例:
public class TestThread extends Thread{
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println("我正在编写多线程代码"+ i);
}
}
//程序主线程 main 线程
public static void main(String[] args) {
//创建子类对象
TestThread thread = new TestThread();
// 设置线程名称
thread.setName("姚青新创建的线程");
//调用 start() 方法开启线程
thread.start();
for (int i = 1; i <= 10; i++) {
System.out.println("我正在学习多线程"+ i);
}
// 获取线程名称
System.out.println(thread.getName());
}
}
运行结果:
public static Thread currentThread(); 返回当前正在执行的线程对象
Demo代码示例:
public class TestThread extends Thread{
@Override
public void run() {
// 获取start()方法创建出来的线程对象
System.out.println("start() 创建的线程对象:"+Thread.currentThread());
// 获取start()方法创建出来的线程对象名称
System.out.println("start() 创建的线程对象名称:"+Thread.currentThread().getName());
}
public static void main(String[] args) {
TestThread thread = new TestThread();
thread.setName("姚青新创建的线程");
thread.start();
//获取 主线程对象
System.out.println("主线程对象:"+Thread.currentThread());
// 获取main()主线程对象名称
System.out.println("主线程对象名称"+Thread.currentThread().getName());
}
}
运行结果:
在使用这个方法的时候需要注意一点,该方法固定的写法就是 Thread.currentThread(); 放在那个线程中执行这个方法就是指定的那个线程。
这个写法主要作用就是获取当前线程的线程对象,获取线程对象之后还可以继续对该线程对象的一些状态进行操作。
Demo 代码示例:
public class TestThread extends Thread{
@Override
public void run() {
System.out.println("这里是 start() 创建的新线程");
try {
// 将线程休眠五秒
Thread.sleep(5000);
System.out.println("将线程休眠五秒");
} catch (InterruptedException e) {
// 如果线程休眠出现异常,就将线程中断
Thread.currentThread().interrupt();
}
}
public static void main(String[] args) {
TestThread thread = new TestThread();
thread.start();
System.out.println("这里是主线程");
Thread.currentThread().interrupt();
System.out.println("将主线程中断");
//判断运行当前线程是否是中断状态
System.out.println("检验当前线程是否是中断状态:"+Thread.currentThread().isInterrupted());
// 将主线程重新连接
System.out.println("当前线程是否需要恢复中断状态:"+Thread.interrupted());
System.out.println("当前线程是否需要恢复中断状态:"+Thread.interrupted());
}
}
运行结果:
通过运行结果可以看出几点:
1、将主线程中断之后,其他线程依然正常执行,也就是说每条线程都是独立的。
2、将当前线程休眠时,当前的线程中的内容同时休眠,休眠倒计时结束线程正常运行。
public final void join(); 当通过线程对象调用该方法时,线程就会呈现堵塞状态,只有调用该方法的线程可以流通。直到线程对象的 run() 执行完毕,线程的堵塞状态结束。
public class ThreadJoin extends Thread {
@Override
public void run() {
for (int x = 0; x < 5; x++) {
System.out.println(getName() + ":" + x);
}
}
public static void main(String[] args) {
ThreadJoin tj1 = new ThreadJoin();
ThreadJoin tj2 = new ThreadJoin();
ThreadJoin tj3 = new ThreadJoin();
tj1.setName("皮卡丘");
tj2.setName("可达鸭");
tj3.setName("呱呱");
tj1.start();
try {
// 将线程切换成堵塞状态,只执行 tj1 线程,run() 执行完成之后,堵塞结束
tj1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
tj2.start();
tj3.start();
}
}
运行结果:可以看出,皮卡丘是先执行的,当这个线程执行完成之后,其他两个线程同步执行。
Demo 代码示例:不调用 yield() 方法看运行的时间差
public class ThreadYield extends Thread{
int count = 0;
@Override
public void run() {
//获得的是自1970-1-01 00:00:00.000 到当前时刻的时间距离,类型为long。
long beginTime =System.currentTimeMillis();
for (int i = 0 ;i<= 50000000; i++) {
//Thread.yield();
count = count +(i+1);
}
long endTime = System.currentTimeMillis();
// 用第二次获取到的时间距离减第一次获取到的时间距离
System.out.println("Time spent by the program is :" +(endTime-beginTime)+"ms");
}
public static void main(String[] args) {
ThreadYield thread = new ThreadYield();
thread.start();
}
}
运行结果:
Demo 代码示例:调用 yield() 方法看运行的时间差
public class ThreadYield extends Thread{
int count = 0;
@Override
public void run() {
//获得的是自1970-1-01 00:00:00.000 到当前时刻的时间距离,类型为long。
long beginTime =System.currentTimeMillis();
for (int i = 0 ;i<= 50000000; i++) {
Thread.yield();
count = count +(i+1);
}
long endTime = System.currentTimeMillis();
// 用第二次获取到的时间距离减第一次获取到的时间距离
System.out.println("Time spent by the program is :" +(endTime-beginTime)+"ms");
}
public static void main(String[] args) {
ThreadYield thread = new ThreadYield();
thread.start();
}
}
运行结果:
通过两次运行结果可以看出程序运行的时间差别还是很大的。调用 yield() 运行的时间明显延长。
java 中线程分为两种类型:用户线程和守护线程。通过 Thread.setDaemon(false); 设置为用户线程;通过 Thread.setDaemon(true); 设置为守护线程。如果不设置次属性,默认为用户线程。
该方法必须在用户线程执行之前执行
用户线程和守护线程的区别:
Demo 代码示例:
public class ThreadsetDaemonDemo extends Thread {
@Override
public void run() {
for (int x = 0; x < 10; x++) {
System.out.println(getName() + ":" + x);
}
}
public static void main(String[] args) {
ThreadDaemon td1 = new ThreadDaemon();
ThreadDaemon td2 = new ThreadDaemon();
td1.setName("皮卡丘");
td2.setName("喷火龙");
// 设置守护线程
td1.setDaemon(true);
td2.setDaemon(true);
td1.start();
td2.start();
Thread.currentThread().setName("小智");
for (int x = 0; x < 5; x++) {
System.out.println(Thread.currentThread().getName() + ":" + x);
}
}
}
运行结果:通过结果可以看出,用户线程一共执行了五次,守护线程也是执行了五次。但是其实守护线程是设置成10次的,因为用户线程执行结束了,所有守护线程自行离开,所有接下来的程序自然也就不继续执行了。
在创建多线程时候推荐使用实现 Runnable 接口的方式,该接口中只有一个 run() 的抽象方法,我们在使用时候只需要实现该抽线方法即可。然后通过实例化 Thread 类,调用 Thread 类的构造方法,然后将我们创建的线程类对象传入该构造方法,并且可以给该线程设置名称,然后调用 Thread 类对象调用 start() 创建和启动线程,从而实现多线程的过程。这是一种代理模式,可以很好的降低程序的耦合。
并且因为接口可以多实现,这就避免了单继承的局限性,灵活便捷,方便同一个对象被多线程使用。
Demo 代码的复用示例:
public class TestRunnable implements Runnable{
//run()方法线程
@Override
public void run() {
// 获取新创建的线程名称
String str = Thread.currentThread().getName();
System.out.println("姚青创建的线程:"+str);
try {
// 将线程休眠4秒
Thread.sleep(4000);
System.out.println(Thread.currentThread().getName()+"休眠4秒");
} catch (InterruptedException e) {
// 如果线程休眠时出现线程异常,就将线程中断
Thread.currentThread().interrupt();
}
}
//程序主线程 main线程
public static void main(String[] args) {
// 创建 Runnable 接口实现类对象
TestRunnable tr = new TestRunnable();
// 创建线程对象,设置线程名称,并通过线程对象来开启线程,这种启动线程的方式是代理的方式
Thread thread = new Thread(tr, "姚青创建的线程");
thread .start();
// 获取当前线程的线程名称
String str = Thread.currentThread().getName();
System.out.println("主线程:" + str);
// 将当前线程中断
Thread.currentThread().interrupt();
System.out.println(Thread.currentThread().getName()+"线程中断了!");
// 判断当前线程是否是中断状态
if (Thread.currentThread().isInterrupted() == true) {
// 如果是中断状态就重新链接
Thread.interrupted();
System.out.println(Thread.currentThread().getName()+"线程重新链接了!");
}
}
}
运行结果: