多线程简单学习
在java中创建线程有两种方式
1.继承Thread,重写run方法;
2.实现Runnable,重写run方法。
继承Thread,重写run方法
/**
* 要求:开启一个线程,每间隔一秒输出"大聪明是只猫"
*/
public class Thread01 {
public static void main(String[] args){
Cat cat = new Cat();
cat.start();
}
}
/**
* 实现方式:继承Thread类,重写run方法;
*/
public class Cat extends Thread{
int num = 0;
@Override
public void run(){
while(true){
System.out.println("大聪明是只猫\t"+ (++num));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num == 8){
break;
}
}
}
}
在上面实例中,实现了“开启一个线程,每间隔一秒输出"大聪明是只猫”。
说明
Thread本身没有run方法,它是实现了Runnable,重写了run方法
示例代码的执行顺序
在Thread01的main方法开始执行时,会创建一个进程,该进程中包含一个线程;当执行到cat.start();会再开启一个新的线程;如果cat.start();下面还有其他代码,该代码会和cat.start();并行执行(是两个独立的线程)。
示例代码为什么不调用cat.run(),而是cat.start()
调用cat.run(),并没有开启一个新的线程,代码还是会从上往下执行(会阻塞),当cat.run()的代码执行完毕,才会继续执行下面的代码;
调用cat.start(),会开启一个新的线程,下面的代码不会阻塞,会和cat中run方法的代码并行执行。
原理是什么
//原理就是源码中的如下关键代码
public synchronized void start() {
/**
该方法是一个本地方法,由JVM进行调用,底层是C或者C++是实现的
private native void start0();
*/
start0();
}
调用start()方法后,会调用start0()方法,该线程并不会立马执行,只是将线程变成了可运行状态,具体什么时候执行,取决于CPU,由CPU统一调度。
实现Runnable接口,重写run方法
实现代码
/**
* @Author Zhang
* @Date 2024/7/28 15:28
* @Version 1.0
* 实现Runnable接口,启动线程
*/
public class Thread02 {
public static void main(String[] args) {
Dog dog = new Dog();
//dog.start(); 不能使用start方法
Thread thread = new Thread(dog);
thread.start();
}
}
public class Dog implements Runnable{
int num = 0;
@Override
public void run() {
while(true){
System.out.println("hi"+ ++num + "\t线程名称"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(num == 10){
break;
}
}
}
}
为什么有了继承Thread方式开启一个线程,还要有实现Runnable接口的方式?
以为java中的单继承机制,如果一个类继承了其父类,还想要重启一个线程,可以使用实现Runnable的方式来开启一个线程。
单继承多实现的机制。
浅谈启动方式
为什么要使用一下方式启动
Thread thread = new Thread(dog);
thread.start();
因为在Runnable中没有start方法,使用静态代理的方式来启动。
模拟静态代理的代码–方便理解为什么实现Runnable接口的线程要通过Thread的start来启动
public class Thread02 {
public static void main(String[] args) {
Pig pig = new Pig();
ProxyThread proxyThread = new ProxyThread(pig);
proxyThread.start();
}
}
class Animal{}
class Pig extends Animal implements Runnable{
@Override
public void run() {
System.out.println("一只猪");
}
}
//可以将ProxyThread理解成Thread
class ProxyThread implements Runnable{
private Runnable target = null;
@Override
public void run() {
if(target != null){
target.run();
}
}
public ProxyThread(Runnable target) {
this.target = target;
}
public void start(){
start0();
}
public void start0(){
run();
}
}
继承Thread和实现Runnable的区别
1.从java设计上来看,继承Thread和实现Runnable接口没有本质的区别,因为Thread本身就实现了Runnable接口
2.实现Runnable接口方式更适合多个线程共享一个资源的情况,并且避免了java中单继承的限制,建议使用实现Runnable接口的方式创建线程
线程终止
基本说明
1.当线程完成任务后,会自动退出;
2.通过变量来控制run方法退出,从而控制线程的退出。
以下代码实现的是通过变量,将线程终止
/**
* 模拟线程退出场景---改变变量,让线程退出
*/
public class ThreadExit {
public static void main(String[] args) throws InterruptedException {
TestThread testThread = new TestThread();
new Thread(testThread).start();
System.out.println("主线程休眠10s");
Thread.sleep(10 * 1000);
testThread.setFlag(false);
}
}
class TestThread implements Runnable{
private boolean flag = true;
public void setFlag(boolean flag){
this.flag =flag;
}
int num = 0;
@Override
public void run() {
while(flag){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程["+Thread.currentThread().getName()+"]运行中\t"+ (++num));
}
}
}
线程常用的方法
线程中断说明
线程中断,是指中断当前线程正在执行的任务,代码示例如下
/**
* 演示Thread中的中断、优先级
*/
public class ThreadMethod01 {
public static void main(String[] args) throws InterruptedException {
Eat eat = new Eat();
Thread thread = new Thread(eat);
thread.start();
System.out.println("要开启的子线程的名称为:"+thread.getName());
System.out.println("要开启的子线程的优先级为:"+thread.getPriority());
//主线程休眠五秒,将子线程中断
System.out.println("主线程中断五秒");
for(int i=0;i<5;i++){
System.out.println("hi"+i);
Thread.sleep(1000);
}
thread.interrupt();//将子线程中断,如果线程在休眠中,会被中断
}
}
class Eat implements Runnable{
@Override
public void run() {
while(true){
for (int i = 0;i < 10;i++){
System.out.println("老猪吃包子"+i);
}
try {
System.out.println(Thread.currentThread().getName()+" 休眠中");
Thread.sleep(10 * 1000);
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+" 被interrupt了");
e.printStackTrace();
}
}
}
}
线程中断说明
线程中断常用的两种方法
yield
线程的礼让。有两个线程A、B,线程A、B同时执行,此时线程A想先让线程B先执行,调用线程A的Thread.yield()方法即可;该方法需要让出CPU,让其他线程先执行,不一定会生效;以为具体需要根据CPU资源是否紧张,当CPU资源紧张时生效概率较大。
public class ThreadMethod02 {
public static void main(String[] args) throws InterruptedException {
ThreadCutLine threadCutLine = new ThreadCutLine();
Thread threadA = new Thread(threadCutLine);
threadA.start();
for(int i=0;i<20;i++){
System.out.println("线程B在执行中\t"+i);
Thread.sleep(50);
if(i == 5){
System.out.println("现成B暂停,先执行线程A");
Thread.yield();//该方法不一定生效
}
}
}
}
class ThreadCutLine implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("线程A在执行中\t"+i);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
join
线程的插队。插队的线程一但成功,则优先执行插队线程的所有任务,插队线程的所有任务执行完成,被插队线程才会继续执行。
例子:有线程A、B,当线程A在执行还未执行完时,希望限制行线程B;此时可以调用B.join()来让线程B先执行;这种情况下,只有线程B执行完成后才会接着执行线程A。
public class ThreadMethod02 {
public static void main(String[] args) throws InterruptedException {
ThreadCutLine threadCutLine = new ThreadCutLine();
Thread threadA = new Thread(threadCutLine);
threadA.start();
for(int i=0;i<20;i++){
System.out.println("线程B在执行中\t"+i);
Thread.sleep(50);
if(i == 5){
System.out.println("现成B暂停,先执行线程A");
threadA.join();
}
}
}
}
class ThreadCutLine implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("线程A在执行中\t"+i);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
守护线程
用户线程和守护线程
1.用户线程:也叫工作线程,当线程的任务执行完时,线程结束;
2.守护线程:一般是为工作线程服务的,当工作线程执行结束时,守护线程自动结束。
3.常见的守护线程:垃圾回收机制。
4.守护线程的作用:创建一个守护线程,监视其他线程的执行情况。
public class ThreadMethod03 {
public static void main(String[] args) throws InterruptedException {
ThreadDaemon threadDaemon = new ThreadDaemon();
Thread thread = new Thread(threadDaemon);
thread.setDaemon(true);//主线程执行结束后,守护线程会自动结束
thread.start();
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
System.out.println("主线程执行中......\t"+i);
}
}
}
class ThreadDaemon implements Runnable{
@Override
public void run() {
for(;;){//无限循环
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("守护线程守护主线程中......");
}
}
}
守护线程错误开启示例
线程的七大状态
线程的状态可以分为七种,也可以分为六种。
六种状态分为:
NEW --尚未开启的线程处于此状态
RUNNABLE(READY RUNNING) --在java虚拟机中执行的线程处于此状态
BLOCK --被阻塞等待监视器锁定的线程处于此状态
WAITING --正在等待另一个线程执行特定动作的线程处于此状态
TIMED_WAITING --正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
TERMINATED --已退出的线程处于此状态
七种状态是将RUNNABLE分为了READY和RUNNING状态。因此,处于RUNNABLE状态下的线程不一是出于运行状态,也可能是出于READY(准备)状态。
代码简单展示
展示的状态有:NEW、RUNNABLE、TIMED_WAITING、TERMINATED
/**
* 线程状态:NEW --尚未开启的线程处于此状态
* RUNNABLE(READY RUNNING) --在java虚拟机中执行的线程处于此状态
* BLOCK --被阻塞等待监视器锁定的线程处于此状态
* WAITING --正在等待另一个线程执行特定动作的线程处于此状态
* TIMED_WAITING --正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
* TERMINATED --已退出的线程处于此状态
*/
public class ThreadMethod04 {
public static void main(String[] args) throws InterruptedException {
ThreadState threadState = new ThreadState();
System.out.println("启动前---子线程:"+threadState.getName()+" 的状态:"+threadState.getState());
threadState.start();
System.out.println("启动后---子线程:"+threadState.getName()+" 的状态:"+threadState.getState());
while(Thread.State.TERMINATED != threadState.getState()){
System.out.println("运行中---子线程:"+threadState.getName()+" 的状态:"+threadState.getState());
Thread.sleep(100);
}
System.out.println("退出状态---子线程:"+threadState.getName()+" 的状态:"+threadState.getState());
}
}
class ThreadState extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("hi\t"+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
线程同步机制
1.在多线程编程中,一些敏感的数据不允许同时被多个线程访问,此时就需要使用同步访问技术,保证数据在任何时刻,最多有一个线程访问,保证数据的完整性。
2.具体方法,使用synchronized关键字
1.同步代码块
synchronized(对象){//得到对象的锁,才能操作同步代码
//需要被同步的代码
}
2.synchronized还可以用在方法声明中,表示整个方法为同步方法
public synchronized void m(){
//需要被同步的代码
}
3.示例
演示售票场景
超售情的代码–未使用synchronized关键字
继承Thread方式实现
该方式,资源不共享
/**
* 继承Thread方式,开启三个线程售卖100张票
* 存在的问题:票会超卖
*/
public class SellTicket01 {
public static void main(String[] args) {
Ticket t1 = new Ticket();
Ticket t2 = new Ticket();
Ticket t3 = new Ticket();
t1.start();
t2.start();
t3.start();
}
}
class Ticket extends Thread{
//因为继承Thread的方式不会共享资源,所以使用static关键字
private static int countTickets = 100;
@Override
public void run() {
while(true){
if(countTickets <= 0){
System.out.println(Thread.currentThread().getName()+"\t停止售票");
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t开始售票,剩余票数:"+ (--countTickets));
}
}
}
实现Runnable接口方式
该方式,资源共享
/**
* 实现Runnable接口,开启三个线程售卖100张票
* 存在的问题:票会超卖
*/
public class SellTicket02 {
public static void main(String[] args) {
Ticket02 t1 = new Ticket02();
new Thread(t1).start();
new Thread(t1).start();
new Thread(t1).start();
}
}
class Ticket02 implements Runnable{
//实现Runnable接口的资源会共享,所以无需static关键字
private int countTickets = 100;
@Override
public void run() {
while (true){
if(countTickets <= 0){
System.out.println(Thread.currentThread().getName()+"\t停止售票");
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t开始售票,剩余票数:"+ (--countTickets));
}
}
}
未超售情况–使用synchronized关键字
/**
* 现成同步机制,synchronized关键字
*/
public class SellTicket03 {
public static void main(String[] args) {
Ticket03 ticket03 = new Ticket03();
new Thread(ticket03).start();
new Thread(ticket03).start();
new Thread(ticket03).start();
}
}
class Ticket03 implements Runnable{
//实现Runnable方式开启线程,资源共享,无需使用static关键字
private int countTickets = 100;
//是否继续收票
public boolean flag = true;
//使用synchronized关键字修饰的方法,在某一时刻只允许有一个线程进行访问
public synchronized void sell(){
if(countTickets <=0){
System.out.println("收票结束...");
flag = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t售出一张票,剩余票数:"+(--countTickets));
}
@Override
public void run() {
while(flag){
sell();
}
}
}
互斥锁
基本介绍
1.Java语言中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。
2.每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任一时刻,只能有一个线程访问该对象。
3.关键字synchronized 来与对象的互斥锁联系。当某个对象用synchronized修饰时表明该对象在任一时刻只能由一个线程访问。
4.同步的局限性:导致程序的执行效率要降低。
5.同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)。
6.同步方法(静态的)的锁为当前类本身。
使用细节
注意事项和细节
1.同步方法如果没有使用static修饰:默认锁对象为this。
2.如果方法使用static修饰,默认锁对象:当前类class。
3.实现的落地步骤:
·需要先分析上锁的代码
·选择同步代码块或同步方法
·要求多个线程的锁对象为同一个即可!
解释–同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
synchronized关键字作用在方法上
此时默认的锁就是this
package com.zwb.syn;
/**
* 现成同步机制,synchronized关键字
*/
public class SellTicket04 {
public static void main(String[] args) {
Ticket03 ticket03 = new Ticket03();
new Thread(ticket03).start();
new Thread(ticket03).start();
new Thread(ticket03).start();
}
}
class Ticket04 implements Runnable{
//实现Runnable方式开启线程,资源共享,无需使用static关键字
private int countTickets = 100;
//是否继续收票
public boolean flag = true;
/*
1.使用synchronized关键字修饰的方法,在某一时刻只允许有一个线程进行访问
此时默认的锁就是this
*/
public synchronized void sell(){
if(countTickets <=0){
System.out.println("收票结束...");
flag = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t售出一张票,剩余票数:"+(--countTickets));
}
@Override
public void run() {
while(flag){
sell();
}
}
}
synchronized作用在代码块上,锁使用this
package com.zwb.syn;
/**
* 现成同步机制,synchronized关键字
*/
public class SellTicket04 {
public static void main(String[] args) {
Ticket03 ticket03 = new Ticket03();
new Thread(ticket03).start();
new Thread(ticket03).start();
new Thread(ticket03).start();
}
}
class Ticket04 implements Runnable{
//实现Runnable方式开启线程,资源共享,无需使用static关键字
private int countTickets = 100;
//是否继续收票
public boolean flag = true;
/*
同步代码块作用在代码块上,锁可以是this
*/
public void sell(){
synchronized (this){
if(countTickets <=0){
System.out.println("收票结束...");
flag = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t售出一张票,剩余票数:"+(--countTickets));
}
}
@Override
public void run() {
while(flag){
sell();
}
}
}
synchronized作用在代码块上,所示其他对象(但为同一个对象)
此时需注意,开启该线程的方式是使用同一个对象,还是使用不同的对象。
1.使用同一个对象的代码如下,锁为其他对象,无需使用static关键字
package com.zwb.syn;
/**
* 现成同步机制,synchronized关键字
*/
public class SellTicket04 {
public static void main(String[] args) {
Ticket03 ticket03 = new Ticket03();
new Thread(ticket03).start();
new Thread(ticket03).start();
new Thread(ticket03).start();
}
}
class Ticket04 implements Runnable{
//实现Runnable方式开启线程,资源共享,无需使用static关键字
private int countTickets = 100;
//是否继续收票
public boolean flag = true;
public Object obj = new Object();
/*
synchronized作用在代码块上,所可以是其他对象(要求是同一个对象)
*/
public /*synchronized*/ void sell(){
synchronized (obj){
if(countTickets <=0){
System.out.println("收票结束...");
flag = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t售出一张票,剩余票数:"+(--countTickets));
}
}
@Override
public void run() {
while(flag){
sell();
}
}
}
2.开启该线程的方式是使用不同的对象。
synchronized 修饰的代码块,不能使用this作为锁即synchronized(this)以为,每个线程使用的不是同一个对象,即:
Ticket03 t1 = new Ticket03();
Ticket03 t2 = new Ticket03();
Ticket03 t3 = new Ticket03();
t1.start();
t2.start();
t3.start();
需使用这种方式:
private static Object obj = new Object();
synchronized (obj){}
/**
* 现成同步机制,synchronized关键字
*/
public class SellTicket03 {
public static void main(String[] args) {
Ticket03 t1 = new Ticket03();
Ticket03 t2 = new Ticket03();
Ticket03 t3 = new Ticket03();
t1.start();
t2.start();
t3.start();
}
}
class Ticket03 extends Thread{
//实现Runnable方式开启线程,资源共享,无需使用static关键字
private static int countTickets = 100;
private static Object obj = new Object();
//是否继续收票
public boolean flag = true;
public void sell(){
synchronized (obj){
if(countTickets <=0){
System.out.println("收票结束...");
flag = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t售出一张票,剩余票数:"+(--countTickets));
}
}
@Override
public void run() {
while(flag){
sell();
}
}
}
解释–同步方法(静态的)的锁为当前类本身
在代码快中不能使用synchronized (this)或者锁为其他对象,只能使用当前代码块所在类作为锁,即:synchronized (Ticket03.class)
public class SellTicket03 {
public static void main(String[] args) {
Ticket03 t1 = new Ticket03();
Ticket03 t2 = new Ticket03();
Ticket03 t3 = new Ticket03();
t1.start();
t2.start();
t3.start();
}
}
class Ticket03 extends Thread{
//实现Runnable方式开启线程,资源共享,无需使用static关键字
private static int countTickets = 100;
//是否继续收票
public static boolean flag = true;
//使用synchronized关键字修饰的方法,在某一时刻只允许有一个线程进行访问
public static void sell(){
synchronized (Ticket03.class){
if(countTickets <=0){
System.out.println("收票结束...");
flag = false;
return;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"\t售出一张票,剩余票数:"+(--countTickets));
}
}
@Override
public void run() {
while(flag){
sell();
}
}
}
线程的死锁
基本介绍:多个线程都占用了对方的锁资源,但不肯想让,导致了死锁,自编程中一定要避免死锁的发生。
/**
* 模拟死锁场景
*/
public class DeadLock_ {
public static void main(String[] args) {
DeadLockThread A = new DeadLockThread(true);
A.setName("线程A");
A.start();
DeadLockThread B = new DeadLockThread(false);
B.setName("线程B");
B.start();
}
}
class DeadLockThread extends Thread{
static Object o1 = new Object();
static Object o2 = new Object();
public boolean flag;
public DeadLockThread(boolean flag){
this.flag = flag;
}
/**
* 一下代码的分析
* 1.当flag为true,先获取o1对象锁,然后再获取o2对象锁;如果只获取到o1对象锁,没获取到o2对象锁,则进入等待--Blocked
* 2.当flag为false,先获取o2对象锁,再获取o1对象锁;如果只获取到o2对象锁,没获取到o1对象锁,则进入等待--Blocked
* 3.当flag先为true,未获取到o2对象锁,进入等待;此时flag为false获取到了o2对象锁,未获取到o1对象锁进入等待;
* 那么就会进入死锁的状态
*
* 在实际编写代码时,应避免一个处理中获取两个不同对象锁的事件发生
*/
@Override
public void run() {
if(flag){
synchronized (o1){
System.out.println(Thread.currentThread().getName()+"\t 进入1");
synchronized (o2){
System.out.println(Thread.currentThread().getName()+"\t 进入2");
}
}
}else{
synchronized (o2){
System.out.println(Thread.currentThread().getName()+"\t 进入3");
synchronized (o1){
System.out.println(Thread.currentThread().getName()+"\t 进入4");
}
}
}
}
}
锁释放
下面操作会释放锁
1.当前线程的同步方法、同步代码执行结束。
2.当前线程在同步代码块、同步方法中遇到break、return。
3.当前线程在同步代码块、同步方法中出现了未处理的Error或者Exception,导致异常结束。
4.当前线程在同步代码块、同步方法中执行了现成编程对象的wait()方法,当前线程暂停,并释放锁。
线面操作不会释放锁
1.程序在执行同步代码块或同步方法时,调用了Thread.sleep()、Thread.yield()方法暂停当前线程的执行,不会释放锁。
2.线程在执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。
提示:应尽量避免使用suspend()和resume()来控制线程,这两个方法JDK已弃用。
Thread.suspend() 方法在Java中用于暂停当前正在执行的线程。当你调用一个线程的 suspend() 方法时,该线程的执行会被暂停,直到其他线程调用它的 resume() 方法为止。
然而,需要指出的是,suspend() 和 resume() 方法在Java中已经被废弃(deprecated),因为它们容易引起死锁问题。使用这两个方法容易导致程序中出现不可预测的行为,因为很难确保 resume() 会在 suspend() 之后及时被调用。如果 resume() 方法没有被调用,那么被 suspend() 暂停的线程将永远等待下去,从而导致程序挂起。
Java官方推荐使用更高级别的并发工具,如 Lock、Semaphore、BlockingQueue 等,或者使用 Object 类的 wait()、notify()、notifyAll() 方法来控制线程间的协作,这些方法提供了更加安全和灵活的方式来处理线程间的同步问题。
线程作业01
要求
1.在main方法中启动两个线程。
2.第1个线程循环随机打印100以内的整数。
3.直到第2个线程从键盘读取了“Q”命令。
代码
package com.zwb.homework;
import java.util.Random;
import java.util.Scanner;
public class HomeWork01 {
public static void main(String[] args) {
HomeThread01 t1 = new HomeThread01();
HomeThread02 t2 = new HomeThread02(t1);
t1.start();
t2.start();
}
}
class HomeThread01 extends Thread{
private volatile boolean loop = true;
public void setLoop(boolean loop) {
this.loop = loop;
}
@Override
public void run() {
while(loop){
Random random = new Random();
int num = random.nextInt(200);
System.out.println(Thread.currentThread().getName()+"随机打印的数为:"+num);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"退出...");
}
}
class HomeThread02 extends Thread{
private HomeThread01 t1;
private Scanner scanner = new Scanner(System.in);
public HomeThread02(HomeThread01 t1){
this.t1 = t1;
}
@Override
public void run() {
while(true){
System.out.println("输入[Q]退出");
String next = scanner.next();
if("Q".equals(next)){
t1.setLoop(false);
System.out.println(Thread.currentThread().getName()+"退出...");
break;
}
}
}
}
注意事项
出错代码
如果HomeThread01中打印的代码位置如下图所示,在HomeThread02将loop的状态改变后还是会再多打印一行:
正确的写法
作业02
要求
(1)有2个用户分别从同一个卡上取钱(总额:10000)
(2)每次都取1000,当余额不足时,就不能取款了
(3)不能出现超取现象 =》 线程同步问题
实现代码–实现Runnable接口方式
package com.zwb.homework;
/**
* (1)有2个用户分别从同一个卡上取钱(总额:10000)
* (2)每次都取1000,当余额不足时,就不能取款了
* (3)不能出现超取现象 =》 线程同步问题.
*/
public class HomeWork0201 {
public static void main(String[] args) {
T t = new T();
Thread t1 = new Thread(t);
t1.setName("t1");
Thread t2 = new Thread(t);
t2.setName("t2");
t1.start();
t2.start();
}
}
class T implements Runnable{
public static int countMoney = 10000;//总金额
private static boolean flag = true;
/**
* 1.使用synchronized实现线程同步
* 2.当多个线程来到这里时,会争夺Money01.class这把锁
* 3.哪个线程先争夺到这把锁,就执行synchronized代码块,执行完后释放Money01.class锁;
* 4.争夺不到Money01.class锁的,就blocked,准备继续争夺
* 5.Money01.class(相当于this)是非公平锁
*/
@Override
public void run() {
while(flag){
synchronized (this){
if(countMoney < 1000){
System.out.println("银行卡余额不足...");
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
countMoney = countMoney - 1000;
System.out.println(Thread.currentThread().getName()+",取了1000块钱后,剩余金额为:"+countMoney);
}
}
}
}
继承Thread方式
package com.zwb.homework;
/**
* (1)有2个用户分别从同一个卡上取钱(总额:10000)
* (2)每次都取1000,当余额不足时,就不能取款了
* (3)不能出现超取现象 =》 线程同步问题.
*/
public class HomeWork02 {
public static void main(String[] args) {
Money01 m1 = new Money01();
Money01 m2 = new Money01();
m1.start();
m2.start();
}
}
class Money01 extends Thread{
private static volatile int countMoney = 10000;//总金额
public static boolean flag = true;
@Override
public void run() {
while(flag){
/**
* 1.使用synchronized实现线程同步
* 2.当多个线程来到这里时,会争夺Money01.class这把锁
* 3.哪个线程先争夺到这把锁,就执行synchronized代码块,执行完后释放Money01.class锁;
* 4.争夺不到Money01.class锁的,就blocked,准备继续争夺
* 5.Money01.class(相当于this)是非公平锁
*/
synchronized (Money01.class){
if(countMoney >= 1000){
countMoney = countMoney - 1000;
System.out.println(Thread.currentThread().getName()+"取了1000块,剩余金额为:" + (countMoney));
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
flag = false;
return;
}
}
}
}
}
就不能取款了
-
(3)不能出现超取现象 =》 线程同步问题.
*/
public class HomeWork02 {
public static void main(String[] args) {
Money01 m1 = new Money01();
Money01 m2 = new Money01();m1.start(); m2.start();
}
}
class Money01 extends Thread{
private static volatile int countMoney = 10000;//总金额
public static boolean flag = true;
@Override
public void run() {
while(flag){
/**
* 1.使用synchronized实现线程同步
* 2.当多个线程来到这里时,会争夺Money01.class这把锁
* 3.哪个线程先争夺到这把锁,就执行synchronized代码块,执行完后释放Money01.class锁;
* 4.争夺不到Money01.class锁的,就blocked,准备继续争夺
* 5.Money01.class(相当于this)是非公平锁
*/
synchronized (Money01.class){
if(countMoney >= 1000){
countMoney = countMoney - 1000;
System.out.println(Thread.currentThread().getName()+"取了1000块,剩余金额为:" + (countMoney));
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
flag = false;
return;
}
}
}
}
}