韩顺平线程自学笔记
程序进程和线程
程序的定义
是为了完成特定任务,用某种语言编写的一组指令的集合。简单的来说就是我们写的代码。
进程的定义
进程是指运行中的程序,比如我们使用的QQ,就启动了一个进程,操作系统就会为该进程分配内存空间,当我们使用迅雷,又启动了一个进程,操作系统将迅雷分配新的内存空间。
进程是程序的一次执行过程,或是正在运行的一个程序,是动态过程:有它自身产生、存在和消亡的过程。
线程的定义
1、线程由进程创建的,是进程的一个实体。
2、一个进程可以拥有多个线程。
单线程
1、单线程:同一个时刻,只允许执行一个线程。
2、多线程:同一个时刻,可以执行多个线程,比如;一个QQ进程可以同时打开多个聊天窗口,一个迅雷进程,可以同时下载多个文件。
3、并发:同一个时刻,多个任务交替执行,造成一种"貌似同时"的错觉,简单的说,单核CPU实现的多任务就是并发。
4、并行:同一个时刻,多个任务同时执行。多核CPU可以实现并行。
5、并发和并行同时存在。
写一段Java程序查看CPU个数
/**
* @author wty
* @date 2022/10/15 23:14
* 写一段Java代码查看CPU个数
*/
public class Fundation {
@Test
public void selectCPU(){
Runtime runtime = Runtime.getRuntime();
int cpunums = runtime.availableProcessors(); // 获得当前电脑的处理器CPU数量
System.out.println("当前电脑CPU个数:"+cpunums);
}
}
创建线程的两种方法
在Java中线程来使用有两种方法。
1、继承Thread类,重写run方法。
2、实现Runnable接口,重写run方法。
继承Thread类
示例:
package com.UseThread;
import org.junit.Test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* @author wty
* @date 2022/10/15 23:20
* <p>
* 继承Thread类,重写run方法。 创建线程
*/
public static void main(String[] args) {
// 创建一个Cat对象当作线程使用
Cat cat = new Cat();
cat.start(); // 启动线程,这里会调用run方法
/**
* 追源码
*
* public synchronized void start() {
* if (this.threadStatus != 0) {
* throw new IllegalThreadStateException();
* } else {
* this.group.add(this);
* boolean var1 = false;
*
* try {
* this.start0(); // 真正实现多线程的方法,是一个private native void start0(); 由JVM虚拟机调用
* var1 = true;
* } finally {
* try {
* if (!var1) {
* this.group.threadStartFailed(this);
* }
* } catch (Throwable var8) {
* }
*
* }
*
* }
* }
*/
//cat.run(); // 这里不直接调用run()是因为,Thread.currentThread().getName() = main,并不是Thread0开启的线程
// 当main线程启动一个子线程的时候,主线程不会阻塞,会继续执行
// 这时主线程main和子线程Thread0会交替进行
System.out.println("主线程名字:" + Thread.currentThread().getName());
for (int i = 1; i <= 50; i++) {
System.out.println("第" + i + "个主线程正在运行!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 继承Thread类,重写run方法。 创建线程
* 线程每隔一秒在控制台输出我是小猫咪
*/
class Cat extends Thread {
int times = 0;
@Override
public void run() {//重写run方法,写上业务逻辑
while (true) {
times++;
System.out.println("喵喵我是一只小猫咪(运行了第" + times + "次)," + "线程名:" + Thread.currentThread().getName());
try {
Thread.sleep(1000); // 休眠1000ms = 1s
} catch (InterruptedException e) {
e.printStackTrace();
}
if (times == 80) {
break; // 运行8次退出
}
} // while结束
}
// run方法的源码,Thread类实现了Runnable接口
/**
* public void run() {
* if (this.target != null) {
* this.target.run();
* }
*
* }
*/
}
cmd 之后输入 jconsole可以查看进程执行情况
线程源码分析图示
实现Runnable接口
说明
- java是单继承的,在某些情况下一个类可能已经继承了某个父类,这时在用继承Thread类方法来创建线程显然是不可能了。
- java设计者们提供了另外一个方式创建线程,就是通过实现Runnable接口来创建线程。
练习要求:
代码示例
package com.UseThread;
import org.junit.Test;
/**
* @author wty
* @date 2022/10/16 11:46
* <p>
* 通过实现Runnable来实现线程创建
* <p>
* 底层实现了静态代理
*/
public class ThreadUse02 {
public static void main(String[] args) {
Dog dog = new Dog();
// 没有start()方法
// 创建Thread对象,把dog对象放入
Thread thread = new Thread(dog);
thread.start();
}
}
class Dog implements Runnable { // 通过实现Runnable接口
int times = 0;
@Override
public void run() { // 普通方法
while (true) {
times++;
System.out.println("小狗汪汪叫了" + times + "声 " + Thread.currentThread().getName());
// 休眠1秒
try {
Thread.sleep(1000); // 1000ms = 1s
} catch (InterruptedException e) {
e.printStackTrace();
}
if (times == 10) {
break;
}
}
}
}
模拟Runnable接口(静态代理)代码示例:
package com.UseThread;
/**
* @author wty
* @date 2022/10/16 11:56
* <p>
* 模拟Runnable接口
* 静态代理
*/
public class MoniRunnable {
public static void main(String[] args) {
Tiger tiger = new Tiger();
ThreadProxy threadProxy = new ThreadProxy(tiger);
threadProxy.start();
}
}
/**
* 当作Thread来看待,模拟了一个极简的Thread类
*/
class ThreadProxy implements Runnable {
private Runnable target = null; // target属性,类型是Runnable
@Override
public void run() {
if (null != target) {
target.run();// 动态绑定
}
}
public ThreadProxy(Runnable target) {
this.target = target;
}
public void start() {
start0(); // 真正实现真线程的方法
}
public void start0() {
run();
}
}
class Animals {
}
class Tiger extends Animals implements Runnable {
@Override
public void run() {
System.out.println("老虎嗷嗷叫!");
}
}
多线程
代码示例:
package com.UseThread;
import javax.print.attribute.standard.DocumentName;
/**
* @author wty
* @date 2022/10/16 16:56
*/
public class ManyThread {
public static void main(String[] args) {
Monkey monkey = new Monkey();
Thread thread1 = new Thread(monkey);
thread1.start();
Dunkey dunkey = new Dunkey();
Thread thread2 = new Thread(dunkey);
thread2.start();
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "执行了第" + i + "次");
}
}
}
class Monkey implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
++count;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("猴子叫了" + count + "声" + Thread.currentThread().getName());
if (10 == count) {
break;
}
}
}
}
class Dunkey implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
++count;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("驴叫了" + count + "声" + Thread.currentThread().getName());
if (5 == count) {
break;
}
}
}
}
继承Thread 和 实现Runnable区别
- 从java的设计来看,通过继承Thread或者实现Runnable来创建线程本质上没有区别,从jdk帮助文档中我们可以看到Thread类本身就实现了Runnable接口。
- 实现Runnable接口方式更加适合多个线程共享一个资源的情况,并且避免了单继承的限制。建议使用Runnable接口
代码示例;
package com.SoldTicket;
/**
* @author wty
* @date 2022/10/16 17:25
* 模拟三个窗口同时售票
*/
public class SellTicket {
public static void main(String[] args) {
/* Ticket ticket1 = new Ticket();
Ticket ticket2 = new Ticket();
Ticket ticket3 = new Ticket();
ticket1.start();
ticket2.start();
ticket3.start();*/
Tickets tickets = new Tickets();
new Thread(tickets).start();
new Thread(tickets).start();
new Thread(tickets).start();
}
}
/**
* 继承Thread接口
*/
class Ticket extends Thread {
public static int ticket = 100; // 定义一个静态常量 100,票数
@Override
public void run() {
while (true) {
if (ticket <= 0) {
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("此时还剩余" + --ticket + "张票" + Thread.currentThread().getName());
}
}
}
/**
* 实现Runnable接口
*/
class Tickets implements Runnable {
public int ticket = 100; // 这里定义普通成员变量即可
@Override
public void run() {
while (true) {
if (ticket <= 0) {
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("此时还剩余" + --ticket + "张票" + Thread.currentThread().getName());
}
}
}
存在问题,会出现超卖情况
那么如何解决呢,需要看后续synchronized的知识
线程终止
- 当线程完成任务后,会自动退出
- 还可以通过使用变量来控制run方法退出的方式停止线程即通知方式
方法一:通知法
package com.ThreadExit;
/**
* @author wty
* @date 2022/10/16 18:17
*/
public class ThreadExit {
public static void main(String[] args) {
Thread_Exit thread_exit = new Thread_Exit();
thread_exit.start();
// 主线程休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread_exit.setLoop(false);
}
}
/**
* 通知的方法,通过改变变量loop通知run方法终止线程
*/
class Thread_Exit extends Thread {
// 定义一个变量来控制循环的开关
private boolean loop = true;
int count = 0;
@Override
public void run() {
while (loop) {
count++;
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程执行第" + count + "次 " + Thread.currentThread().getName());
}
}
// 定义set方法
public void setLoop(boolean loop) {
this.loop = loop;
}
}
线程的常用方法
代码示例:
package com.ThreadMethod;
/**
* @author wty
* @date 2022/10/16 18:34
* 线程的常用方法
*/
public class ThreadMethod {
public static void main(String[] args) {
ThreadMethodExercise threadMethodExercise = new ThreadMethodExercise();
// 设置线程名称使之与参数name相同
threadMethodExercise.setName("ThreadMethodExercise");
// 更改线程的优先级,优先级范围:
// Thread.MAX_PRIORITY 10,Thread.MIN_PRIORITY 1,Thread.NORM_PRIORITY 5
/** 源码:
* public static final int MIN_PRIORITY = 1;
* public static final int NORM_PRIORITY = 5;
* public static final int MAX_PRIORITY = 10;
*/
threadMethodExercise.setPriority(Thread.NORM_PRIORITY);
// 使该线程开始执行
// 注意细节:start底层会创建新的线程,调用run,
threadMethodExercise.start();
// 通知的方式终止线程
//threadMethodExercise.setLoop(false);
// 返回线程的名称
System.out.println(threadMethodExercise.getName());
// 调用线程对象的run方法,run就是一个简单的方法调用,不会刷新线程
// threadMethodExercise.run();
// 获取线程的优先级
System.out.println(threadMethodExercise.getPriority());
for (int i = 1; i <= 5 ; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 中断线程,不是停止线程。一般用于中断正在休眠的线程
// 当执行到这里时,会中断threadMethodExercise的休眠
threadMethodExercise.interrupt();
}
}
class ThreadMethodExercise extends Thread {
private boolean loop = true;
int count = 0;
@Override
public void run() {
while (loop) {
count++;
try {
// 在指定的毫秒内让当前线程休眠(暂停)
// sleep:线程的静态方法,使当前线程休眠
/** 源码:
* public static native void sleep(long var0) throws InterruptedException;
*/
System.out.println(Thread.currentThread().getName() + "休眠中");
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被Interrupt中断了");
}
System.out.println(Thread.currentThread().getName() + " 线程执行了第" + count + "次");
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
代码示例:
package com.ThreadMethod;
/**
* @author wty
* @date 2022/10/16 19:08
* 线程的常用方法02
*/
public class ThreadMethod02 {
public static void main(String[] args) {
ThreadMethodExercise02 threadMethodExercise02 = new ThreadMethodExercise02();
Thread thread = new Thread(threadMethodExercise02);
thread.start();
for (int i = 1; i <= 20; i++) {
try {
// System.out.println(Thread.currentThread().getName() + "休眠中");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程 " + Thread.currentThread().getName() + "执行了第" + i + "次");
if (i == 5) {
System.out.println("----main线程礼让Thread-0线程----");
try {
/**
* join():线程的插队
* 线程的插队一旦成功,则先执行完插入的线程所有的任务
*
*/
thread.join();
/**
* Thread.yield();
* 线程的礼让:让出CPU,让其他线程执行,但是礼让的时间不确定。所以也不一定礼让成功
*
*/
Thread.yield();
System.out.println("----Thread-0线程全部运行完毕,main接着运行----");
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* Thread.yield();
* 线程的礼让:让出CPU,让其他线程执行,但是礼让的时间不确定。所以也不一定礼让成功
*
*/
Thread.yield();
System.out.println("----Thread-0线程全部运行完毕,main接着运行----");
}
}
}
}
class ThreadMethodExercise02 implements Runnable {
int count = 0;
@Override
public void run() {
while (true) {
count++;
try {
//System.out.println(Thread.currentThread().getName() + "休眠中");
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + "被Interrupt中断了");
}
System.out.println(Thread.currentThread().getName() + " 线程执行了第" + count + "次");
if (20 == count) {
break;
}
}
}
}
课后练习
代码示例;
package com.ThreadMethod;
/**
* @author wty
* @date 2022/10/16 19:45
*/
public class ThreadMethodHomeWork {
public static void main(String[] args) {
SonThread sonThread = new SonThread();
Thread thread = new Thread(sonThread);
//thread.start();
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hi " + i);
if (i == 5) {
try {
thread.start(); // 启动子线程
thread.join(); // 启动后立即插队
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("主线程结束");
}
}
class SonThread implements Runnable {
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello " + i);
}
System.out.println("子线程结束");
}
}
用户线程和守护线程
- 用户线程:可以理解为是系统的工作线程,它会完成这个程序需要完成的业务操作。如我们使用 Thread 创建的线程在默认情况下都属于用户线程,当线程的任务执行完毕或者通知方式结束。
- 守护线程:是一种特殊的线程,在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT 线程都是守护线程。守护线程一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束。
- 常间的守护线程:垃圾回收机制
代码示例:把一个线程设置成守护线程
正常情况:t是m的子线程,假如m结束了,t该怎么执行怎么执行,不会结束。现在把t作为守护线程,当m结束后,t要跟着一起结束。
代码示例:
package com.WorkThread;
/**
* @author wty
* @date 2022/10/16 20:04
* <p>
* 守护线程
*/
public class WorkThread {
public static void main(String[] args) {
MyDaemonThread myDaemonThread = new MyDaemonThread();
// 主线程
// 现在需要主线程结束后,子线程跟着一起结束
// 那么需要把子线程设置成守护线程
myDaemonThread.setDaemon(true);
myDaemonThread.start();
for (int i = 1; i <= 10; i++) {
System.out.println("main主线程在运行~");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 守护线程
*/
class MyDaemonThread extends Thread {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("MyDaemonThread正在执行~");
}
}
}
线程的生命周期
JDK中用Thread.State枚举表示了线程的几种状态
1、NEW:尚未启动的线程处于此状态
2 、RUNNABLE:在Java虚拟机中执行的线程处于此状态
3 、BLOCKED:被阻塞等待监视器锁定的线程处于此状态 4、WAITING:正在等待另一个线程执行特定动作的线程处于此状态
5、TIMEED_WAITING:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
6、TERMINATED:已退出的线程处于此状态
线程转换图
线程转换的代码示例:
package com.ProgrammerState;
/**
* @author wty
* @date 2022/10/16 23:09
*/
public class ThreadState {
public static void main(String[] args) {
T t = new T();
System.out.println(t.getName() + "状态:" + t.getState());
t.start();
while (Thread.State.TERMINATED != t.getState()) {
System.out.println(t.getName() + "状态:" + t.getState());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(t.getName() + "状态:" + t.getState());
}
}
class T extends Thread {
@Override
public void run() {
while (true) {
for (int i = 1; i <= 10; i++) {
System.out.println("hi " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
}
线程的同步
- 在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在同一时刻,最多有一个线程访问,以保证数据的完整性。
- 也可以这么理解:线程同步,即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作,其他线程才能对该内存地址进行操作。
###同步的具体方法–synchronized
1.同步代码块
synchronized(对象){ // 得到对象的锁,才能操作同步代码
// 需要被同步的代码
}
2.synchronized还可以放在方法声明中,表示整个方法-为同步方法
public synchronized void m (String name){
// 需要被同步的代码
}
3.就好像,某小伙伴上厕所前先把门关上,完事儿后再出来,那么其它小伙伴就可以继续使用厕所了。
这里再把上面买票问题进行梳理,用synchronized来解决,代码示例:
package com.SoldTicket;
/**
* @author wty
* @date 2022/10/16 17:25
* 模拟三个窗口同时售票
*/
public class SellTicket {
public static void main(String[] args) {
// Ticket ticket1 = new Ticket();
// Ticket ticket2 = new Ticket();
// Ticket ticket3 = new Ticket();
// ticket1.start();
// ticket2.start();
// ticket3.start();
Tickets tickets = new Tickets();
new Thread(tickets).start();
new Thread(tickets).start();
new Thread(tickets).start();
}
}
/**
* 继承Thread接口
*/
class Ticket extends Thread {
public static int ticket = 100; // 定义一个静态常量 100,票数
public boolean loop = true;
@Override
public void run() {
while (loop) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
}
sell();
}
}
public synchronized void sell() {
if (ticket <= 0) {
loop = false;
return;
}
System.out.println("此时还剩余" + --ticket + "张票" + Thread.currentThread().getName());
}
}
/**
* 实现Runnable接口
*/
class Tickets implements Runnable {
public int ticket = 1000; // 这里定义普通成员变量即可
private boolean loop = true;
@Override
public void run() {
while (loop) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
sell();
}
}
public synchronized void sell() {
if (ticket <= 0) {
loop = false;
return;
}
System.out.println("此时还剩余" + --ticket + "张票" + Thread.currentThread().getName());
}
}
分析同步原理
锁
互斥锁
基本介绍
- Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
- 每个对象都在对应于一个可称为"互斥锁"的标记,这个标记用来保证在任意时刻,能有一个线程访问该对象。
- 关键字synchronized来与对象的互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任意时刻只能由一个线程访问
- 同步的局限性:导致程序的执行效率降低
- 同步方法(非静态)的锁可以是this,也可以是 其它对象(要求是同一个对象)
- 同步方法(静态的)的锁为当前类本身。
代码示例:
package com.SynchronizedUse;
/**
* @author wty
* @date 2022/10/17 0:48
*/
public class SynchronizedUse {
public static void main(String[] args) {
A a = new A();
a.start();
}
}
class A extends Thread{
private static int count = 0;
Object object = new Object();
@Override
public void run() {
//代码块同步
synchronized (this){
}
// 同类的其它对象
synchronized (object){
}
}
public static void getExample(){
//静态代码块同步
synchronized (A.class){
}
}
}
互斥锁的注意事项
- 同步方法如果没有使用static修饰,默认锁对象为this
- 如果方法使用static修饰,默认锁对象:当前类.class
- 实现的落地步骤:
(1)需要先分析上锁的代码
(2)选择同步代码块或同步方法
(3)要求多个线程的锁对象为同一个即可
线程的死锁
基本介绍
每个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程是一定要避免死锁的发生。
代码示例:
package com.DeadLock;
/**
* @author wty
* @date 2022/10/17 15:36
* <p>
* 模拟死锁过程
*/
public class DeadLock {
public static void main(String[] args) {
DeadLockExercise deadLockExercise1 = new DeadLockExercise(true);
deadLockExercise1.setName("A线程");
DeadLockExercise deadLockExercise2 = new DeadLockExercise(false);
deadLockExercise2.setName("B线程");
deadLockExercise1.start();
deadLockExercise2.start();
}
}
class DeadLockExercise extends Thread {
static Object object1 = new Object();
static Object object2 = new Object();
private boolean loop;
@Override
public void run() {
/**
* 业务逻辑分析
* 1.如果flag为T,线程A就会先得到/持有object1对象锁,然后尝试获取object2对象锁
* 2.如果线程A得不到object2对象锁,就会Blocked
* 3.如果flag为F,线程B就会先得到/持有object2对象锁
*/
if (loop) {
synchronized (object1) {
System.out.println(Thread.currentThread().getName() + " 1对象出口1");
synchronized (object2) {
System.out.println(Thread.currentThread().getName() + " 2对象出口1");
}
}
} else {
synchronized (object2) {
System.out.println(Thread.currentThread().getName() + " 2对象出口2");
synchronized (object1) {
System.out.println(Thread.currentThread().getName() + " 1对象出口2");
}
}
}
}
public DeadLockExercise(boolean loop) {
this.loop = loop;
}
}
释放锁
下面操作会释放锁
- 当前线程的同步方法、同步代码块执行结束
案例:上厕所,完事出来 - 当前线程在同步代码块、同步方法中遇到break、return。
案例:没有正常的完事,经理叫他修改bug,不得已出来 - 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,并释放锁。
案例:没有正常完事,觉得需要酝酿下,所以出来等会儿再进去。
下面操作不会释放锁
- 线程执行代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁
案例:上册错,太困了,在坑上眯了一会儿 - 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。
提示:应该尽量避免使用suspend()和resume()来控制线程,方法不再推荐使用。
课后作业
课后作业01
方法一:守护线程
package com.RealHomeWork;
import org.junit.Test;
import java.util.Scanner;
/**
* @author wty
* @date 2022/10/17 17:03
*/
public class RealHomeWork01 {
public static void main(String[] args) {
Thread01 thread01 = new Thread01();
thread01.setName("线程A");
thread01.setDaemon(true);
thread01.start();
Thread02 thread02 = new Thread02();
Thread thread = new Thread(thread02);
thread.setName("线程B");
thread.start();
}
}
/**
* 随机打印100以内的整数
*/
class Thread01 extends Thread {
@Override
public void run() {
while (true) {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
randomNum();
}
}
@Test
public void randomNum() {
System.out.println((int) (Math.random() * 100 + 1) + " " + Thread.currentThread().getName());
}
}
/**
* 键盘输入Q
*/
class Thread02 implements Runnable {
private boolean loop = true;
public boolean isLoop() {
return loop;
}
public void setLoop(boolean loop) {
this.loop = loop;
}
@Override
public void run() {
while (loop) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
ScannerText();
}
}
public void ScannerText() {
System.out.println(Thread.currentThread().getName() + "请在键盘输入一个数:");
Scanner scanner = new Scanner(System.in);
char c = scanner.next().charAt(0); // 获取输入的第一个字符
if ('\0' !=c && 'Q' == c) { // c不为空,且c是Q
loop = false;
return;
}
}
}
方法二:通过调用对象的形式
package com.RealHomeWork;
import java.util.Scanner;
/**
* @author wty
* @date 2022/10/17 17:51
*/
public class RealHomeWork02 {
public static void main(String[] args) {
A a = new A();
a.setName("线程A");
a.start();
B b = new B(a);
Thread thread = new Thread(b);
thread.setName("线程B");
thread.start();
}
}
class A extends Thread {
private boolean loop = true;
@Override
public void run() {
while (loop) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + (int) (Math.random() * 100 + 1));
}
}
public void setLoop(boolean loop) {
this.loop = loop;
}
}
class B implements Runnable {
private A a;
private Scanner scanner = new Scanner(System.in);
private boolean flag = true;
public B(A a) {
this.a = a;
}
@Override
public void run() {
while (flag) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "请输入任意字符(输入Q退出)");
char c = scanner.next().charAt(0);
if (c != '\0' && 'Q' == c) {
flag = false;
a.setLoop(false);
return;
}
}
}
}
课后作业02
代码示例:方法一用实现Runnable接口实现
package com.RealHomeWork;
/**
* @author wty
* @date 2022/10/17 19:13
*/
public class RealHomeWork03 {
public static void main(String[] args) {
Card card = new Card();
Thread thread1 = new Thread(card);
thread1.setName("线程A");
Thread thread2 = new Thread(card);
thread2.setName("线程B");
thread1.start();
thread2.start();
}
}
class Card implements Runnable {
public boolean loop = true;
public static int money = 10000;
@Override
public void run() {
while (loop) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (this){
money = getMoney(money);
}
}
}
public int getMoney(int money) {
if (money <= 1000) {
loop = false;
System.out.println("卡里的钱已经取完!");
return money;
}
money = (money - 1000);
System.out.println(Thread.currentThread().getName() + "取了1000块,还剩下" + money + "元");
return money;
}
}
代码示例:用继承Thread类实现
package com.RealHomeWork;
/**
* @author wty
* @date 2022/10/17 22:55
*/
public class RealHomeWork04 {
public static void main(String[] args) {
BorrowMoney borrowMoney1 = new BorrowMoney();
borrowMoney1.setName("线程A");
BorrowMoney borrowMoney2 = new BorrowMoney();
borrowMoney1.setName("线程B");
borrowMoney1.start();
borrowMoney2.start();
}
}
class BorrowMoney extends Thread {
public boolean loop = true;
public static int money = 10000;
public static Object object = new Object();
@Override
public void run() {
while (loop) {
synchronized (object){
if (money <= 1000) {
loop = false;
System.out.println("钱已经全部取出!");
return;
}
money = money - 1000;
System.out.println(Thread.currentThread().getName() + "取走了1000块,还剩" + money + "块");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}