Java多线程学习4

本文详细介绍了Java线程的五个生命周期阶段:新建、就绪、运行、阻塞和死亡,以及各阶段之间的状态转换。同时,讨论了线程调度,包括线程优先级、休眠、让步、加入和同步方法。通过实例展示了线程同步的实现,如同步代码块和同步方法,以及死锁和多线程通信的概念。最后,通过生产者消费者问题展示了线程通信的重要性及其解决方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1 线程的生命周期以及状态转换

线程的生命周期包括五个阶段

1 新建状态:创建了一个新的线程对象

2 就绪状态:线程对象创建后,其他线程调用了该对象的start()方法,该状态的线程位于可运行线程池中,变得可运行,等待CPU的使用权

3 运行状态:就绪状态的线程获取了CPU的使用权,执行程序代码

4 阻塞状态:线程因为某种原因放弃了CPU的使用权,暂时停止运行,直到线程进入就绪状态,才有机会转到运行状态

阻塞的状态分为三种情况:

        1.等待阻塞:运行的线程执行wait()方法,jvm会把该线程放入等待池里,wait会释放持有的锁

        2.同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,jvm会把该线程放入锁池中

        3.其他阻塞:运行的线程执行sleep方法或者join方法时或者发出了I/O请求,JVM会把该线程设置为阻塞状态

5 死亡状态:线程执行完或因为异常退出了run方法,该线程结束生命周期

线程的调度

1 线程的优先级

Java线程优先级用整数表示 1-10 数字越大优先级越高

Thread类提供了三个静态常量表示线程的优先级

static int MAX_PRIORITY  ------10

static int MIN_PRIORITY   ------1

static int NORM_PRIORITY   -------5

线程的优先级不是固定不变的,可以通过setPriority(int newPriority)方法对它进行设置

thread.setPriority(Thread.MAX_PRIORITY);

thread.setPriority(5);

2 线程休眠

如果想人为控制线程的执行顺序,可以让正在执行的线程暂停,用sleep方法

代码演示

package com.jian;

public class Thread01 extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "正在输出i: "+ i);
            if (i == 2) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

-------------------------
package com.jian;

public class Thread02 extends Thread{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName() + "正在输出i: "+ i);
        }
    }
}

-------------------------
package com.jian;

public class Test {
    public static void main(String[] args) {
        Thread01 thread01 = new Thread01();
        Thread02 thread02 = new Thread02();
        thread01.start();
        thread02.start();
    }
}

输出

Thread-0正在输出i: 0
Thread-1正在输出i: 0
Thread-0正在输出i: 1
Thread-1正在输出i: 1
Thread-1正在输出i: 2
Thread-1正在输出i: 3
Thread-1正在输出i: 4
Thread-0正在输出i: 2
Thread-1正在输出i: 5
Thread-1正在输出i: 6
Thread-1正在输出i: 7
Thread-1正在输出i: 8
Thread-1正在输出i: 9
Thread-0正在输出i: 3
Thread-0正在输出i: 4
Thread-0正在输出i: 5
Thread-0正在输出i: 6
Thread-0正在输出i: 7
Thread-0正在输出i: 8
Thread-0正在输出i: 9

可以看到 Thread-0因为休眠了5秒,使用Thread-0 剩下的都在最后执行

3 线程让步

Thread.yield()方法,暂停当前正在执行的线程对象,把执行机会让给相同或者优先级更高的线程

因为Java虚拟机默认是抢占式调度模型,所有线程都会抢占CPU资源,所以在执行线程让步时,并不能保证立即执行其他线程

4 线程加入

join()方法  在某个线程中调用其他线程的join方法,则当前线程进入阻塞状态,直到另外一个线程执行完毕,这个线程才会继续执行

package com.jian;

public class Thread03 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 6; i++) {
            System.out.println(Thread.currentThread().getName() + "输入: "+i);
        }
    }
}

class Example{
    public static void main(String[] args) {
        Thread t = new Thread(new Thread03(),"thread1");
        t.start();
        for (int i = 0; i < 6;i++) {
            System.out.println(Thread.currentThread().getName() + "输入: "+i);

            if (i == 2) {
                try {
                    t.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}

输出

main输入: 0
thread1输入: 0
main输入: 1
thread1输入: 1
thread1输入: 2
thread1输入: 3
thread1输入: 4
thread1输入: 5
main输入: 2
main输入: 3
main输入: 4
main输入: 5

可以看到,调用了t的join方法,所以main线程等待t线程执行完才开始执行

5 多线程同步

同步代码块:

synchronized关键字

package com.Sale;

public class SaleThread implements Runnable{

    private int ticket = 10;
    Object lock = new Object();
    @Override
    public void run() {
        while (true) {
            synchronized (lock) {
                if (ticket > 0) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "正在发售第: " + ticket-- + "张票");
                }
            }
        }
    }
}

class Example1{
    public static void main(String[] args) {
        SaleThread saleThread = new SaleThread();
        new Thread(saleThread,"售票窗口1").start();
        new Thread(saleThread,"售票窗口2").start();
        new Thread(saleThread,"售票窗口3").start();
        new Thread(saleThread,"售票窗口4").start();

    }
}

同步方法:

package com.Sale;

public class SaleThread01 implements Runnable{

    private int ticket = 10;
    Object lock = new Object();

    private synchronized void saleTicket(){

        if (ticket > 0 ) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "正在发售 "+ ticket--+"张票");
        }

    }


    @Override
    public void run() {
        while (true) {
            saleTicket();
        }
    }
}

class Example11{
    public static void main(String[] args) {
        SaleThread saleThread = new SaleThread();
        new Thread(saleThread,"售票窗口1").start();
        new Thread(saleThread,"售票窗口2").start();
        new Thread(saleThread,"售票窗口3").start();
        new Thread(saleThread,"售票窗口4").start();

    }
}

6 同步锁

死锁演示

代码解释: A线程用着lock1 B线程用着lock2 A线程要结束还需要lock2锁 B线程要结束还需要lock1锁,但是由于A和B都无法释放自己占据的锁,所以程序死锁了

package com.deadlock;

/**
 * 死锁演示
 */
public class Deadlock implements Runnable {

    static Object lock1 = new Object();
    static Object lock2 = new Object();
    private Boolean flag;

    Deadlock(Boolean flag){
        this.flag = flag;
    }


    @Override
    public void run() {

        if(flag){
            while (true) {
                synchronized (lock1){
                    System.out.println(Thread.currentThread().getName() + "");
                    synchronized (lock2){
                        System.out.println(Thread.currentThread().getName() + "");
                    }
                }
            }
        }else {
            while (true) {
                synchronized (lock2){
                    System.out.println(Thread.currentThread().getName() + "");
                    synchronized (lock1){
                        System.out.println(Thread.currentThread().getName() + "");
                    }
                }
            }
            
        }

    }
}

class Example{
    public static void main(String[] args) {
        Deadlock deadlock1 = new Deadlock(true);
        Deadlock deadlock2 = new Deadlock(false);
        new Thread(deadlock1,"A").start();
        new Thread(deadlock2, "B").start();

    }
}

多线程通信

案例 生产者消费者 生产顺序不一致问题

package com.conmunication;

import java.util.ArrayList;
import java.util.List;

/**
 * 多线程通信案例,生产者,消费者
 */

public class Example{
    public static void main(String[] args) {

        List<Object>goods = new ArrayList<Object>();
        long start = System.currentTimeMillis();

        //创建一个生产者线程
        Thread thread1 = new Thread(()->{
            int num = 0;
            while(System.currentTimeMillis()-start<=100){
                goods.add("商品" + ++num);
                System.out.println("生产产品" + num);

            }
        },"生产者");

        //创建一个消费者线程
        Thread thread2 = new Thread(()->{
           int num = 0;
           while (System.currentTimeMillis() - start <= 100) {
               goods.remove("商品"+ ++num);
               System.out.println("消费商品" +num);
           }
        },"消费者");

        thread1.start();
        thread2.start();

    }
}

输出

生产产品1
消费商品1
生产产品2
消费商品2
生产产品3
生产产品4
生产产品5
生产产品6
消费商品3

并没有生产一个就消费一个,顺序乱掉了

为了解决上面的问题,需要用到线程通信,让线程知道自己什么时候该干什么事情

线程通信的常用方法

1 .void wait() :使当前线程放弃同步锁并进入等待,直到其他线程进入此同步锁,并调用notify()或者notifyAll()方法唤醒该线程为止

2:void notify():唤醒此同步锁上等待的第一个调用wait()方法的线程

3:void notifyALL() 唤醒此同步锁上调用wait()方法的全部线程

使用线程通信方法优化生产者消费者程序

package com.conmunication;

import java.util.ArrayList;
import java.util.List;

/**
 * 多线程通信案例,生产者,消费者
 */

public class Example02{
    public static void main(String[] args) {

        List<Object>goods = new ArrayList<Object>();
        long start = System.currentTimeMillis();

        //创建一个生产者线程
        Thread thread1 = new Thread(()->{
            int num = 0;
            while(System.currentTimeMillis()-start<=100){
                //同步代码块
                synchronized (goods){
                    //如果goods里面的商品大于0,让生产者线程暂时等待
                    //等待消费者线程把商品消费到0,然后继续执行生产者
                    if(goods.size()>0){
                        try {
                            goods.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }else{
                        goods.add("商品" + ++num);
                        System.out.println("生产商品"+num);
                    }
                }

            }
        },"生产者");

        //创建一个消费者线程
        Thread thread2 = new Thread(()->{
            int num = 0;
            while (System.currentTimeMillis() - start <= 100) {
                synchronized (goods) {
                    //如果good里面的商品等于0,就暂停消费者线程,让生产者线程进行生产
                    //当商品大于0时,再启动消费者线程,消费商品
                    if (goods.size() <= 0) {
                        goods.notify();

                    }else{
                        goods.remove("商品"+ ++num);
                        System.out.println("消费商品" +num);
                    }
                }
            }
        },"消费者");

        thread1.start();
        thread2.start();

    }
}

输出正常

生产商品1
消费商品1
生产商品2
消费商品2
生产商品3
消费商品3
生产商品4
消费商品4
生产商品5
消费商品5
生产商品6
消费商品6
生产商品7
消费商品7
生产商品8
消费商品8
生产商品9
消费商品9
生产商品10
消费商品10
生产商品11
消费商品11
生产商品12
消费商品12
生产商品13
消费商品13

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值