1.自己利用线程的第一种实现方式,实现如下功能(多线程引例):
a. 程序不停地在屏幕上输出一句问候的语句(比如“你好”)(时间间隔一定要大些比如3s(或大于3s),因为在控制台窗口,输入和输出不能同时发生,我们只能在两次输出“你好”的间隙,从键盘输入数据,才能保证键盘输入被正确接收)。
b.同时,当我通过键盘输入固定响应的时候,程序停止向屏幕输出问候的语句。
代码:
public class MultiThread {
static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
sayHelloRecycling();
waitToStop();
System.out.println("main end");
}
/**
* 在子线程中,接收键盘输入,并根据键盘输入
* ,决定是否终止,在屏幕上输出问候语句
*/
private static void waitToStop() {
new Thread() {
@Override
public void run() {
Scanner scanner = new Scanner(System.in);
while (true) {
String s = scanner.nextLine();
if ("gun".equals(s)) {
flag = false;
scanner.close();
break; //如果输入了gun,就终止程序的运行
}
}
}
}.start();
}
/**
* 在子线程中,不停的在屏幕上输出问候语句,
* 直到,循环的控制变量flag的值被改为false
*/
private static void sayHelloRecycling() throws InterruptedException {
new Thread() {
@Override
public void run() {
while (flag) {
System.out.println("哈哈, 你好!");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
运行结果:
2.阅读如下代码:
1)回答输出的是什么内容
输出的是:Thread匿名子类的run方法
2)为什么
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(“Runnable匿名子类的run方法”);
}
}) {
@Override
public void run() {
System.out.println(“Thread匿名子类的run方法”);
}
}.start();
解答:
原因如下:
-
首先,我们创建了一个Thread的匿名内部类对象
-
该匿名Thread子类对象接收一个Ruannable接口子类的匿名内部类对象
-
在Thread子类的匿名内部类定义中,我们覆盖了父类(Thread)的run方法
-
接着我们在Thread的匿名内部类对象上调用了start()方法,启动该Thread匿名内部类对象所表示的子线程
-
在该子线程(即Thread的匿名内部类对象)上调用start()方法,start()方法会调用Thread类的run()方法,但这个run方法在Thread的匿名子类定义中被子类覆盖了。所以实际在子线程中执行并非是Thread类的run()方法,而是Thread的匿名子类中定义的run()方法(即多态效果)
3.用多线程代码来模拟,迅雷用3个线程下载100M资源的的过程。
每个线程每次,一次下载1兆(M)资源,直到下载完毕,即剩余的待下载资源大小为0 (用一个整数表示资源大小,每次个线程每次下载多少兆(M), 剩余待下载资源就减少多少兆(M),考虑多线程的数据安全问题)。
代码:
package xxx;
public class ThreadSafe {
public static void main(String[] args) {
Download download = new Download();
new Thread(download, "线程1").start();
new Thread(download, "线程2").start();
new Thread(download, "线程3").start();
}
}
class Download implements Runnable{
int resources = 50;
private Object lock = new Object();
@Override
public void run() {
while (resources > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock){
if(resources > 0){
System.out.println(Thread.currentThread().getName() +
"下载了1M资源,未下载资源还有" + --resources +"M");
if(resources == 0){
System.out.println("资源下载完毕!");
}
}
}
}
}
}
运行结果:
3.创建2个线程,打印从0到99这100个数字,要求线程交叉顺序打印。(要求利用线程间通信api实现)
比如:
线程1: 0
线程2: 1
线程1: 2
线程2: 3
线程1: 4
线程2: 5
代码:
//参考答案
public class Work2 {
/**
*
* 既然是两个线程交替打印,那就是一个线程打印奇数,一个线程打印偶数
* 奇数线程打印奇数,然后让被打印数字增1
* 偶数线程打印偶数,然后让被打印数字增1
* 知道打印到题目中数字的上界99
*/
public static void main(String[] args) {
//创建PrintNumber对象,该对象记录了被打印数字,同时,创建该对象时,指定打印数字的上界
PrintNumber printNumber = new PrintNumber(99);
OddTask oddTask = new OddTask(printNumber);
EvenTask evenTask = new EvenTask(printNumber);
new Thread(evenTask, "线程1").start();
new Thread(oddTask, "线程2").start();
}
}
/*
定义一个类,封装被打印的数字
*/
class PrintNumber {
private int i = 0;
//指定被打印数字的上线,在题目中upBound的取值为99,只要打印到99即可
private int upBound;
public PrintNumber(int upBound) {
this.upBound = upBound;
}
/*
让被打印数字增1
*/
public void add() {
i++;
}
/*
获取打印数字的上界
*/
public int getUpBound() {
return upBound;
}
/*
获取被打印数字当前的值
*/
public int value() {
return i;
}
}
/*
打印奇数,和偶数线程的公共父类,打印的主要逻辑都在该类中
子类只实现,具体的打印条件的判断即可,可以很大程度的复用代码
*/
abstract class PrintTask implements Runnable {
/*
奇数偶数线程共同打印的对象,该对象中包含被打印数字的值
*/
protected PrintNumber number;
public PrintTask(PrintNumber number) {
this.number = number;
}
@Override
public void run() {
//如果还没打印到最大值,就接着循环打印
while (number.value() <= number.getUpBound()) {
synchronized (number) {
if (number.value() > number.getUpBound() ) {
//如果超出打印数字的上界,就退出循环不打印
break;
}
if (!shouldPrint()) {
//如果不满足打印条件,当前线程就不打印
try {
number.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
// 满足当前线程的打印条件
//打印数字的值
System.out.println(Thread.currentThread().getName() + " i = " + number.value());
//使被打印数字的值增1
number.add();
if (number.value() > number.getUpBound()) {
//被打印的数字的值,已经超过了了上界,即打印完了
System.out.println("打印完毕");
}
// 通知其他等待的打印线程,继续打印
number.notifyAll();
}
}
}
}
// 该方法由子类实现,判断当前线程是否该打印数字i的值
protected abstract boolean shouldPrint();
}
/*
打印奇数
*/
class OddTask extends PrintTask {
public OddTask(PrintNumber number) {
super(number);
}
/*
打印奇数的线程,当number对象中,i的值为奇数时才打印
*/
@Override
protected boolean shouldPrint() {
return number.value() % 2 != 0;
}
}
/*
打印偶数
*/
class EvenTask extends PrintTask {
public EvenTask(PrintNumber number) {
super(number);
}
/*
打印偶数的线程,当number对象中,i的值为偶数时才打印
*/
@Override
protected boolean shouldPrint() {
return number.value() % 2 == 0;
}
}
运行结果:
#个人学习记录,如发现有错误之处,欢迎与我交流