多线程2

本文详细介绍了Java多线程的创建方式(继承Thread和实现Runnable),区分前台线程和后台线程,探讨了线程状态、等待、中断机制以及线程安全问题。重点强调了线程调度的随机性和并发执行可能带来的结果不确定性。

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

目录

多线程

1. Thread创建的写法

  1.继承Thread,重写run--->描述线程执行的任务

 2.实现Runnable,重写run

3.继承Thread,重写run,使用内部匿名类

4实现Runnable,重写run,使用内部匿名类

5使用lambda方法

前台线程和后台线程

是否存活

是否会被中断

​编辑

线程的等待

线程状态


多线程

进程是系统资源分配的基本单位
线程是系统调度执行的基本单位

一个进程可以包含一个线程,也可以包含多个线程(不能包含 0 个线程)之前学习过的都是单线程。

1. Thread创建的写法

  1.继承Thread,重写run--->描述线程执行的任务

public class Test5 {
    public static void main(String[] args) throws InterruptedException {
    Thread thread=new Mythead();
    thread.start();
    thread.run();
    Thread.sleep(1000);
    }
}
class  Mythead extends Thread{
    @Override
    public void run(){
        System.out.println("hello thread");
    }
}

 2.实现Runnable,重写run

使用Runnable表示任务的具体情况--》解耦合

public class Test6 {
    public static void main(String[] args) throws InterruptedException {
    Mythread mythread=new Mythread();
    mythread.run();
    Thread.sleep(1000);
    }
}
class  Mythread implements Runnable{

    @Override
    public void run() {

    }
}

3.继承Thread,重写run,使用内部匿名类

1.创建一个 Thread 的子类,(叫啥名字不知道)

2.同时也创建了这个子类的实例
3.重写 run 方法,

public class Test7 {
    public static void main(String[] args) throws InterruptedException {
       Thread thread=new Thread(){
           @Override
           public void run(){
               System.out.println("1211231");
           }
       };
       thread.start();
       Thread.sleep(100000);
    }

}

4实现Runnable,重写run,使用内部匿名类

1.创建新的类,实现 Runnable.但是类的名字是匿名的

2.创建了这个新类的实例.(一次性)

3.重写 run 方法,

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

        Thread thread=new Thread(new Runnable(){
            @Override
            public void run(){
                System.out.println("线程正在1111");
            }
        };
        thread.start();
    }
}

5使用lambda方法

public class Test9 {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
           for (int i=0;i<100;i++){
               System.out.println(1111);
           }
        });
        thread.start();
        Thread.sleep(10000);
    
    }
}

Thread中的一些核心属性和方法

  1. id
  2. name
  3. demon(后台线程)
  4. 状态---getState
  5. 优先级---priority
  6. isAlive—判断系统内核中的线程是否存在
  7. 后台线程--》isDaemon
  8. start 启动线程
  9. 线程终止写法isinterruptted(即使出现sleep等阻塞状态也会被提前唤醒)

前台线程和后台线程

前台线程: 这样的线程如果不运行结束的话,此时java 进程是一定不会结束的
后台线程: 这样的线程,即使继续在执行,也不能阻止 java 进程结束。

如果main方法执行完成,并且thread线程为后台线程的时候,没有thread的堵塞方法,那么thread很有可能完成不了。

前台最后一个进程结束的时候,总进程就会结束。进程就会被gc回收。

public class Test9 {
    public static void main(String[] args) throws InterruptedException {
        int count = 0;
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 100; i++) {
                count++;
            }
        });

        // 将线程设置为后台线程
        thread.setDaemon(true);

        thread.start();
        Thread.sleep(10000);
        System.out.println(count);
    }
}

是否存活

指的是系统中的线程(PCB)是否还存在,

t.start方法才是真正在系统中创建出的先成功

一个线程需要先通过 run /lambda 把线程要完成的任务,定义出来.star 才是真正创建线程, 并开始执行。

核心就是是否真的创建线程出来,每个线程都是独立调度执行的.(相当于整个程序中多了一个执行流)

一个Thread对象,只能star一次

是否会被中断

即终止。t线程在执行的时候,其他线程

终止线程,在 Java 中, 都只是"提醒,建议",真正要不要终止,还得线程本体来进行决定的!!

这里所指的不是强制终止。如果强制终止

pcb是内核中线程的体现

1.自己来实现控制线程结束代码的例子,

思路:让线程的入口方法尽快执行结束。

2.)使用 Thread 提供的 interrupt 方法 和 isInterruptted 方法,来实现上述的效果

t.interrupt(); 通过这个方法,就相当于是设置 boolean 值为 true

使用这个方法会使线程直接进入跳出状态,这时候就会抛出一个InterruptedException 异常,不会再等待,立即就唤醒了,然后可以在异常中写出像写出的代码

线程的等待

多个线程,调度的顺序在系统是无序的,通过线程等待就可以将线程结束的顺序确定出来

通过t.join的方法就可以将结束顺序固定

假设在main线程调用t.join,main线程就会等待t,t结束后,main也结束了。

t线程没有结束,join堵塞等待,等到t结束后,join就会解除堵塞状态,继续执行,

。(堵塞:该线程不参与cpu的调度执行,解除堵塞继续执行,线程参与到cpu的调度。)

可以理解为两个线程换为一前一后的一个线程。

 同时调用t1.join和t2.join的时候,先执行t1。join等待t1线程执行完毕,结束后继续执行t2。join等待t2结束main就会结束。但是t1和t2的线程是在main执行的时候也在执行,他只是等待结束,并不是有了main执行到了join才开始。

d9e5a24ab1934338a7e6df0c3f6ab137.png

线程是并发执行的

t1 和t2是各自调度各自执行

85761527de7d48e5829a1f1a02fe8c78.png

执行先后顺序/谁先结束,都是不确定只不过上述代码中,t1 sleep 3s, t2 sleep 4s

2b45a2c410c0411588b7e41e0de0f38c.png

此时 t1 先结束了t2后结束了

所以在执行main方法的时候会在t1join后再等待大约1s(堵塞在t2),才会结束。如果反过来就会变得t2join后,t1。join直接通过。

98610156277f45d8b6ad1d4a2f4488be.png

可以通过对main的方法,用joint1,同时在t1中用joint2,这样就完成了t2对t1的顺序排序。

join() 死等

join(long mills)等到nms 

join(long mills,int naps)等到n毫秒m纳秒。

时间戳:System.currentTimeMills();

通常用sleep方法让他堵塞,防止线程打乱顺序。

在seep的时间到了时候,线程会从堵塞状态恢复到就绪状态。

线程状态

也就是PCB的状态。

1.NEW Thread 对象有了,还没调用 start
系统内部的线程还未创建.
2.TERMINATED 线程已经终止了.
terminated
内核中的线程已经销毁了,
3RUNNABLE 就绪状态.    
指的是,这个线程“随叫随到'
a) 这个线程正在 CPU 上执行
b) 这个线程虽然没在CPU 上执行,随时可以调度到 CPU 上执行.

4WAITING
死等 进入的阻塞

5TIMED_WAITING 带有超时时间的等

6BLOCKE
进行锁竞争的时候产生的阻塞

08460394274a49da80d109f5d1eb3161.png

线程安全问题!!![核心& 难点 &考点]

线程 是随机调度,抢占式执行.=>这样的随机性,就会使程序的执行顺序,产生变数 =>不同的结果.
但是有的时候,週到不同的緒果、认为不可接收。这为是bug)

701657ab209c4402953292ddc28eebc6.png

如果不加join,直接打印,由于三个线程完全并发执行
此时就会使 主线程 打印 count 并非是 t1 t2 算完的效果.(类似于 读脏数据了

1)把内存 count 中的数值,读取到 cpu 寄存器中,load

2) 把寄存器中的值,+1,还是继续保存在寄存器中 add
3) 把寄存器上述计算后的值,写回到内存 count 里 save

6e693551aea04ecc88dfa8dd327fbe9f.png

通过上述执行过程,发现,两次++
最后内存的結果、仍然是1!!!
后一次计算 把前一次计算的结果,覆盖掉
ア!!
由于当前线程执行的顺序不确定,有些执行顺序,加两次,结果正确的有些执行顶序,加两次,最后只增加 1
具体有多少次正确,多少次不正确,随机的!!
因此看到的结果,不是精确的 Sw!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鱼裤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值