多线程详解04:多线程基础练习
文章目录
练习一:并行、并发概念
问题:请简单描述什么是并行,什么是并发?
答:
并发:一个CPU同一时间段内处理多个线程任务。并发的关键是你有处理多个任务的能力,不一定要同时。
并行:多个CPU同时处理多个线程任务,这些线程之间彼此互不影响。并行的关键是你有同时处理多个任务的能力。
所以它们最关键的点就是:是否是同时。
练习二:进程概念、线程概念、线程与进程联系
问题:请描述什么是进程,什么是线程,进程与线程之间的关系,并举例说明。
答:
进程:可执行程序的一次执行过程,它是一个动态概念,是系统分配资源的单位,比如在Windows系统中,一个运行的xx.exe就是一个进程。
线程:
进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。**线程是CPU调度和执行的单位。**例如微信程序中发起的每个聊天框都是一条线程。
练习三
问题:请描述Thread类中的*start()方法与run()*方法的区别。
答:
start()方法的作用是使线程启动, run()方法中写的是线程启动后的执行内容,且run()方法必须是public的访问权限,返回类型为void。*start()方法启动线程后自动调用run()*方法;
练习三 :多线程编码练习1
问题:
请编写程序,分别打印主线程的名称和子线程的名称。
要求使用两种方式实现:
第一种方式:继承Thread类。
第二种方法:实现Runnable接口。
答:
public class Demo05 {
public static void main(String[] args) {
String name1 = Thread.currentThread().getName();
System.out.println(name1);
//实现Thread类
new Thread(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}.start();
//实现Runnable接口
new Thread(
()->{
System.out.println(Thread.currentThread().getName());
}
).start();
}
}
输出结果:
main
Thread-0
Thread-1
练习四:多线程编码练习2:多线程匿名内部类练习
问题:
编写程序,创建两个线程对象,一根线程循环输出“播放背景音乐”,另一根线程循环输出“显示画面”,要求线程实现Runnable接口,且使用匿名内部类实现
答:
public class Demo06 {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("播放背景音乐");
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("显示画面");
}
}
}).start();
}
}
练习五:多线程编码练习3
问题:
创建多线程对象,开启多线程。在子线程中输出1-100之间的偶数,主线程输出1-100之间的奇数。
答:
public class Demo07 {
public static void main(String[] args) {
for (int i = 1; i < 101; i++) {
if (i%2!=0){
System.out.println(i);
}
}
new Thread(
()-> {
for (int i = 1; i < 101; i++) {
if (i%2!=0){
System.out.println(i);
}
}
}
).start();
}
}
练习六:多线程编码练习4
问题:
请按要求编写多线程应用程序,模拟多个人通过一个山洞:
1.这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒;
2.随机生成10个人,同时准备过此山洞,并且定义一个变量用于记录通过隧道的人数。
显示每次通过山洞人的姓名,和通过顺序;
答:
问题分析:
- sleep(5000)
- 10条线程
- 同步代码块解决线程安全问题:如多个线程同时卖票时可能相同号码的票被卖了多次
synchronized(锁对象) {
原子操作的代码;【有可能出现线程安全问题的代码】
} - 锁对象可以是任何引用类型的对象(如 Object类的对象obj)
- 锁对象一旦被确定下来要保证唯一性
- 优点和缺点
- 代码示例
- 好处:解决了多线程的数据安全问题
- 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序
的运行效率
public class Hello implements Runnable{
@Override
public void run(){
synchronized(this){
try{
Thread.sleep(5000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"通过了山洞");
}
}
public static void main(String[] args){
Hello p=new Hello();
new Thread(p,"赵一").start();
new Thread(p,"赵二").start();
new Thread(p,"赵三").start();
new Thread(p,"赵四").start();
new Thread(p,"赵五").start();
new Thread(p,"赵六").start();
new Thread(p,"赵七").start();
new Thread(p,"赵八").start();
new Thread(p,"赵九").start();
new Thread(p,"赵十").start();
}
}
操作步骤描述
1. 定义一个隧道类,实现Runnable接口:
1.1 定义一个变量,用来记录通过隧道的人数;
1.2 重写Runnable的run方法;
1.3 定义一个同步方法,模拟每个人通过隧道需要5秒钟:
1.3.1 子线程睡眠5秒钟,模拟每个人通过隧道需要5秒钟;
1.3.2 改变通过的人次;
1.3.3 打印线程名称及其通过隧道的顺序,模拟人通过隧道及其顺序;
1.4 调用通过隧道的方法;
2. 定义一个测试类:
2.1 在main方法中创建一个隧道类对象;
2.2 在main方法中,循环创建10个子线程对象,通过构造方法把隧道对象
和线程名(作为人的姓名)传递进去,并开启子线程;
答:
public class Tunnel implements Runnable{
int num = 0;
@Override
public void run() {
synchronized (this){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"通过了隧道,此时共通过了"+ ++num+"个人");
}
}
}
public class Test {
public static void main(String[] args) {
Tunnel tunnel = new Tunnel();
new Thread(tunnel,"赵1").start();
new Thread(tunnel,"赵2").start();
new Thread(tunnel,"赵3").start();
new Thread(tunnel,"赵4").start();
new Thread(tunnel,"赵5").start();
new Thread(tunnel,"赵6").start();
new Thread(tunnel,"赵7").start();
new Thread(tunnel,"赵8").start();
new Thread(tunnel,"赵9").start();
new Thread(tunnel,"赵10").start();
}
}