合工大后端训练营学习心得--java多线程

本文介绍了Java中的线程和进程概念,线程调度的抢占式和均分式模型。Java采用抢占式调度。文章详细讲解了Java实现多线程的两种方式,即继承Thread类和实现Runnable接口,并分析了线程的生命周期。此外,还讨论了线程的sleep(),interrupt(),yield()和join()方法,以及Object类的wait(),notify(),notifyAll()方法。最后,文章提到了多线程并发环境下数据安全问题及synchronized关键字用于实现线程互斥。

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

要了解java多线程,首先需要我们理清楚一个问题:

什么是线程,什么是进程?

        进程是一个应用程序。

        线程是一个进程中的执行场景/执行单元。

当然一个进程也可以启动多个线程。

线程调度:  

  • 抢占式调度模型:
    那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。
    java采用的就是抢占式调度模型

  • 均分式调度模型:
    平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
    平均分配,一切平等。
    有一些编程语言,线程调度模型采用的是这种方式。

关于java线程的生命周期:

 

在java中,实现多线程有两种方式:

        1.编写一个类,继承java.lang.Thread。

        2.实现java.lang.Runnable接口。

java线程构造方法

构造方法名 备注 Thread() Thread(String name) name为线程名 Thread(Runnable target) Thread(Runnable target,String name) name为线程名

这两种方法都必须重写run()方法,不同的是,当线程启动时,第一个方法可以直接调用线程对象的start()方法,而第二种方法需要使用实现Runnable接口的对象作为参数构造一个Thread类的对象,并调用start()方法。

eg.

// 定义一个类继承Thread类
public class MyThread extends Thread{
	public void run(){
	
	}
}
// 创建线程对象
MyThread t = new MyThread();
// 启动线程。
t.start();

注意:

        t.run() 不会启动线程,只是普通的调用方法而已。不会分配新的分支栈。(这种方式就是单线程。)

        t.start() 方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。
        这段代码的任务只是为了开启一个新的栈空间,只要新的栈空间开出来,start()方法就结束了。线程就启动成功了。
        启动成功的线程会自动调用run方法,并且run方法在分支栈的栈底部(压栈)。
run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。

// 定义一个类实现Runnable接口
public class MyRunnable implements Runnable {
	public void run(){
	
	}
}
// 创建线程对象
Thread t = new Thread(new MyRunnable());
// 启动线程
t.start();

注意:
        实现接口比较常用,因为一个类实现了接口,它还可以去继承其它的类,更灵活。

run()方法不可以throws向上抛出异常,只能在方法中使用try--catch进行异常处理

获取当前线程对象、线程对象名字的获取和修改

方法名 作用 static Thread currentThread() 获取当前线程对象 String getName() 获取线程对象名字 void setName(String name) 修改线程对象名字

线程没有被手动设置名字时,其默认的名字为:

        Thread-0

        Thread-1

        Thread-2

             ...

        以此类推

线程的sleep方法

方法名 作用 static void sleep(long millis) 休眠当前线程millis毫秒

注意:

        静态方法:Thread.sleep(1000);
        参数是毫秒
        作用: 让当前线程进入休眠,进入“阻塞状态”,放弃占有CPU时间片,让给其它线程使用。

eg.

public class ThreadTest06 {
    public static void main(String[] args) {
        // 睡眠1秒
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

关于线程的interrupt()方法

方法名 作用 void interrupt() 中断线程

注意:

        interrupt()不能中断在运行中的线程,它只能改变中断状态而已。实际完成的是让受阻塞的线程退出阻塞状态。

关于线程的yield()方法

方法名 作用 static void yield() 让位方法,使当前线程暂停,让出CPU时间片,回到就绪状态。

注意:

        yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。

        yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”,在回到就绪之后,有可能还会再次抢到

eg.

public class ThreadTest12 {
    public static void main(String[] args) {
        Thread t = new Thread(new MyRunnable6());
        t.setName("t");
        t.start();

        for(int i = 1; i <= 10000; i++) {
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

class MyRunnable6 implements Runnable {

    @Override
    public void run() {
        for(int i = 1; i <= 10000; i++) {
            //每100个让位一次。
            if(i % 100 == 0){
                Thread.yield(); // 当前线程暂停一下,让给主线程。
            }
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}

关于线程的join()方法

方法名 作用 void join() 将一个线程合并到当前线程中,当前线程受阻塞,加入的线程执行到终止 void join(long millis) 同上,等待该线程终止的时间最长为millis毫秒 void join(long millis,int nanos) 同上,等待该线程终止的时间最长millis毫秒+nanos纳秒

eg.

public class ThreadTest13 {
    public static void main(String[] args) {
        System.out.println("main begin");

        Thread t = new Thread(new MyRunnable7());
        t.setName("t");
        t.start();

        //合并线程
        try {
            t.join(); // t合并到当前线程中,当前线程受阻塞,t线程执行直到结束。
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("main over");
    }
}

class MyRunnable7 implements Runnable {

    @Override
    public void run() {
        for(int i = 0; i < 10000; i++){
            System.out.println(Thread.currentThread().getName() + "--->" + i);
        }
    }
}
Object类的wait()、notify()、notifyAll()方法
方法名作用
void wait()让活动在当前对象的线程无线等待(释放之前占有的锁)
void notify()唤醒当前对象正在等待的线程(并不会释放锁)
void notifyAll()唤醒当前对象全部正在等待的线程(并不会释放锁)

注意:

        wait和notify方法不是线程对象的方法,是java中任何一个java对象都有的方法,因为这两个方法是 Object 类中的。wait方法和notify方法不是通过线程对象调用

 多线程并发环境下数据安全问题:

        当出现如下条件时,会出现数据安全问题:

                条件1:多线程并发

                条件2:有共享数据

                条件3:共享数据有修改的行为

线程安全问题的解决方法:

        线程排队执行,用排队执行解决线程安全问题。

        这种机制被称为:线程同步机制,实际上就是线程不能并发了,线程必须排队执行。

        但是这种方法会使线程无法并发,会牺牲效率。

为解决这类问题,java提供了一个关键字:synchronized

多线程互斥的实现

        对于访问某个关键共享资源的所有方法,都必须把它们设为 synchronized,例如:

synchronized void f() { 
    /* ... */ 
}

synchronized void g() {
    /* ... */
}

        当一个线程A使用一个synchronized修饰的方法时,其它线程想使用这个方法时就必须等待,直到线程A 使用完该方法 (除非线程A主动让出CPU资源)。

        如果想保护某些资源不被多个线程同时访问,可以强制通过 synchronized方法访问那些资源。

        ▪ 调用synchronized方法时,对象就会被锁定

        ▪ 当synchronized方法执行完或发生异常时,会自动释放锁

        ▪ 被synchronized保护的数据应该是私有(private)

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值