今天做点JAVA多线程的练习题来巩固一下多线程的相关知识,话不多说,直接上题。
Thread类中的start()方法与run()方法的区别
答:线程对象调用run()方法不开启线程,仅仅是对run()方法的调用。而start()方法会开启线程,并且让jvm调用run()方法在线程中执行。
在做这道题的时候,我发现了一个比较有趣的事情,下面两段代码的运行结果居然不是一样的,惊到我了。
public class Demo11 extends Thread{
public static void main(String[] args){
new Demo11().start();
System.out.println("--------------");
new Demo11().run();
}
@Override
public void run() {
System.out.println("abc");
}
}
--------------
abc
abc
public class Demo11 extends Thread{
public static void main(String[] args){
new Demo11().run();
System.out.println("--------------");
new Demo11().start();
}
@Override
public void run() {
System.out.println("abc");
}
}
abc
--------------
abc
第一段代码结果居然是先输出分隔的“----------”,想一想可能是start()方法会创建一个线程,而创建这个线程是需要时间的,所以导致了先输出了下一行代码。第二段代码先调用run()方法,这个几乎不消耗时间,所以会先执行。
那么这个想法是否是对的呢?
将上述run方法中的代码加上时间获取就可以知道线程运行的时间了。由于光输出一行耗的时间太少了,改写了下内容。
public void run() {
long start = System.currentTimeMillis();
int sum = 0;
for(int i = 0; i < 100;i++){
sum += i;
System.out.println(sum);
}
long end = System.currentTimeMillis();
System.out.println("线程执行的时间:"+(end-start));
}
由于代码结果比较长,就不截图了,直接说结论。
每次线程的运行时间都可能会有变化,总体来说,run方法和start方法在这个程序中耗时无明显差别。
但是run方法在前面的时候,会执行完run方法再输出分割线,是严格按照自上而下的顺序来执行的。
而start方法在第一行的时候,直接就输出了分隔线。
另外,我知道我这个设计思路有一个问题,得到的时间只是运行所花的时间,而创建线程是不是需要时间,需要多少时间是不知道的。这个问题就到这儿吧,超出我的认知范围了,记住结论就好。start方法会开启线程,run方法只是调用。好,下一题。
两种方式创建多线程
这道题目比较基础,也没有太多要思考的点,直接放上代码。
第一个实现Runnable方法
public class Demo10 implements Runnable {
@Override
public void run() {
for(int i = 0;i<10;i++){
Thread.currentThread().setName("th02");
System.out.println(Thread.currentThread().getName()+"第"+i+"次输出");
}
}
}
继承Thread类方法
public class Demo09 extends Thread{
public static void main(String[] args){
Thread th01 = new Demo09();
th01.start();
Runnable runnable = new Demo10();
Thread th02 = new Thread(runnable);
th02.start();
}
@Override
public void run() {
for(int i = 0; i < 10;i++){
currentThread().setName("th01");
System.out.println(currentThread().getName()+"第"+i+"次输出");
}
}
}
在线程的生命周期中,有几种状态?
NEW(新建)
线程刚刚被创建,但是未使用
Runnable(可运行)
线程可以在java中运行的状态 ,但是线程有没有运行并不知道
Blocked(锁阻塞)
当线程获取的对象是有锁的状态,且该对象被其他线程线程持有,则该线程进入Blocked状态;当线程有持有锁的时候变成Runnable状态
Waiting(无限等待)
一个线程在等待另一个线程执行唤醒动作时,该线程进入Waiting状态。进入Waiting状态是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒
Timed Waiting(计时等待)
不需要无限等待另一个线程调用唤醒,到了设定的时间会自动唤醒
Teminated(被终止)
因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡
匿名内部类创建多线程
问题:
编写程序,创建两个线程对象,一根线程循环输出“播放背景音乐”,另一根线程循环输出“显示画面”,要求线程实现Runnable接口,且使用匿名内部类实现
public static void main(String[] args) {
//1.新建第一条线程并开启
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("播放背景音乐");
}
}
}).start();
//2.新建第二条线程并开启
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
System.out.println("显示画面");
}
}
}).start();
}
请按要求编写多线程应用程序,模拟多个人通过一个山洞
要求:
1.这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒;
2.随机生成10个人,同时准备过此山洞,并且定义一个变量用于记录通过隧道的人数。显示每次通过山洞人的姓名,和通过顺序;
public class Tunnel implements Runnable {
// 1.1 定义一个变量,用来记录通过隧道的人数
private int crossNum = 0;
/*
* 1.2 重写Runnable的run方法
*/
@Override
public void run() {
// 1.4 调用通过隧道的方法
cross();
}
/*
* 1.3 定义一个同步方法,模拟每个人通过隧道需要5秒钟
*/
public synchronized void cross() {
// 1.3.1 子线程睡眠5秒钟,模拟每个人通过隧道需要5秒钟
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 1.3.2 改变通过的人次
crossNum++;
// 1.3.3 打印线程名称及其通过隧道的顺序,模拟人通过隧道及其顺序
System.out.println(Thread.currentThread().getName() + "已经通过隧道,TA是第" + crossNum + "通过的!");
}
}
public class TunnelDemo {
public static void main(String[] args) {
// 2.1 在main方法中创建一个隧道类对象
Tunnel tul = new Tunnel();
// 2.2 在main方法中,循环创建10个子线程对象,通过构造方法把隧道对象和// 线程名(作为人的姓名)传递进去,并开启子线程
for (int i = 1; i <= 10; i++) {
Thread t = new Thread(tul, "p" + i);
t.start();
}
}
}