多线程简单学习

多线程简单学习

在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;
            }
        }
    }
}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值