Skip to content

多线程基础

约 1550 字大约 5 分钟

java多线程

2025-02-14

1 run和start

先上代码来看两者区别

package test;

public class ThreadTest {

     public static void main(String args[]) {        
         ThreadSync syncThread1 = new ThreadSync();
         ThreadSync syncThread2 = new ThreadSync();
         Thread thread1 = new Thread(syncThread1, "SyncThread1");
         Thread thread2 = new Thread(syncThread2, "SyncThread2");
         thread1.run();
         thread2.start();
     }
}
package test;

public class ThreadSync implements Runnable{

    public void run() {
        function();
    }

    public synchronized static void function() {
        for (int i = 0; i < 5; i++) {
            try {
                System.out.println(Thread.currentThread().getName() + ":" + i);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
main:0
main:1
main:2
main:3
main:4
SyncThread2:0
SyncThread2:1
SyncThread2:2
SyncThread2:3
SyncThread2:4

run就是一个普通的方法,还是调用的主线程,并没有创建制定的新线程。start是线程执行的方法,创建进线程。

2 线程的状态

**1、新增(New)**线程创建后,尚未启动的线程。就是没有执行start()的线程。

**2、就绪(Runnable)**在执行了start()之后,线程进入线程池,等待OS调用获得CPU的使用权,并不是说执行了t.start()此线程立即就会执行。

**3、运行(Running)**CPU开始调度处于就绪状态的线程,就绪状态是进入到运行状态的唯一入口。

**4、阻塞(Blocked)**等待阻塞:调用了wait()方法。同步阻塞:获取synchronized同步锁失败(因为锁被其它线程所占用)。其他阻塞:调用线程的sleep()或join()或发出了I/O请求,处理完毕时线程重新转入就绪状态。

**5、结束(Dead)**已终止线程的状态,线程已经结束执行。当一个线程的run方法走完,或者主线程的main方走完,就是终止了。单也许这个线程对象是活的。在一个终止的线程上调用start方法,会抛出java.lang.IllegalThreadStateException异常。

image-20250214165527515

3 sleep和wait

sleep:方法来自于thread类;不释放对象锁;需要捕获异常;任何地方都可以用。wait:方法来自于object类;释放对象锁;不需要捕获异常;只有在同步代码块/方法中才能使用。

1 sleep注意的点:

sleep的Thread类中的静态方法。静态方法意味着什么?举个例子:A线程有一个sleep(),B线程有一个sleep()。A线程调用B线程的sleep(),那么谁睡着了?答案是A。因为sleep()方法其实不分你我,它是大家的,谁用谁睡。在sleep()休眠时间期满后,该线程不一定会立即执行,这是因为其它线程可能正在运行而且没有被调度为放弃执行,除非此线程具有更高的优先级。

2 不释放对象锁 和 释放对象锁 意味着什么?

不释放对象锁意味着线程仍旧处于同步状态,不允许任何线程调用sleep()外的synchronized。当睡醒之后等待CPU时间片控制分配。释放对象锁,其他线程可以进入synchronized方法执行。并且只能被唤醒之后才能有权利继续执行,注意是有权利。就是说回到线程争取CPU时间的位置上。

3 wait为啥必须在synchronized里使用?

首先执行wait之后是需要notify或者notifyAll唤醒的。这三个方法为啥必须在synchronized的关键字里面?因为wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。wait与notify是针对已经获取了锁进行操作。

4 既然上面提到wait的唤醒,那么这个过程怎么理解?

对于Java虚拟机中运行程序的每个对象来说,都有两个池,锁池和等待池。执行wait之后,线程进入等待池,等待池里的线程不会去争夺锁的占有权,需要别人来把它从等待池里拉出来。当调用的notify或者notifyAll时,该方法把线程从等待池来出来放回到锁池,在锁池里可以公平争夺锁的占有权了。那它这么费劲儿的去拿锁的占有权干啥?当然是为了最后的CPU使用时间。谁不想早点干完活下班?

5 wait和sleep的区别

wait()和sleep()都可以通过interrupt()方法 打断线程的暂停状态 ,从而使线程立刻抛出InterruptedException。

如果线程A希望立即结束线程B,则可以对线程B对应的Thread实例调用interrupt方法。如果此刻线程B正在wait/sleep /join,则线程B会立刻抛出InterruptedException,在catch() {} 中直接return即可安全地结束线程。

需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用 interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到 wait()/sleep()/join()后,就会立刻抛出InterruptedException 。

4 interrput和yield

interrupt

调用interrupt(),通知线程应该中断了。这个很有意思,是通知线程应该中断,而不是中断线程,是一个中断信号。当通知线程的时候,线程当时的状态不同,反应不同。1.如果线程正处于正常活动的状态,那么interrupt()会将该线程的中断标志设置为true,被设置中断标志后,线程仍然继续运行。2.如果是正处于阻塞,或者正常运行之后阻塞了,线程会立即退出被阻塞状态,并抛出一个InterruptedException。

让线程在无限等待时(如死锁时)能抛出抛出,从而结束线程,但是如果你吃掉了这个异常,那么这个线程还是不会中断的。但是可以在catch() {} 中直接return即可安全地结束线程。

yield

暂停当前正在执行的线程对象,并执行其他线程。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把CPU的占有权交给此线程,否则,继续运行原来的线程。