欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

与线程的第一次见面

程序员文章站 2024-02-26 09:13:07
...

1、线程

2、Java中线程的创建

3、线程生命周期及线程状态之间的转换

4、线程的常用方法

1、那,什么是线程呢?

闲谈:
学习Java到现在,作为一个身份是一丢丢的程序员的我来说,写过了许多叫做程序的东西。我们写好一段代码,然后运行,然后得到预期的结果。
我想,对于线程陌生,那么进程就会是一个稍微贴切的概念。
对于平时用的QQ啊、微信啊、微博啊、网易云音乐,都可以称为一个进程,对于实现它们的那些代码,则叫做程序。
进程,是系统资源分配和处理机调度的一个独立单位。
那么,线程,则可以称为轻量级的线程。

比如说,你打开了咪咕音乐,这是一个进程。对于它来说,你可以在听歌的时候去下载歌曲,还可以去读歌曲下的评论,或者去做一些别的事,对于咪咕音乐这个进程来说,你的这些操作就可以称为一个个线程。
之所以引入线程,是因为进程的创建与撤销的花销比较大,在不追求安全和稳定的情况下,那么我们可以只启动线程也能完活。
说简单点,现在电脑的性能越来越好了,为了对得起电脑的内存利用率和系统吞吐量,都在追求高并发,无疑,多线程是实现这样需求的一种很好的思路。

对于线程来说,其实它本身并不拥有系统资源,它只需要有一点必不可少的、能保证独立运行的资源,即允许多个线程共享该进程所拥有的资源。
那么对应到Java里来说,
**方法区、堆:当前所有线程之间所共享
每个线程都有自己的程序计数器、栈(Java栈和本地方法栈)

2、Java中创建一个线程的三种方式

首先,无论是什么方法,都要重写一个run()方法。
而启动线程,则是调用start()方法

1)继承Thread类,重写run()方法

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println("hello world");
    }
}

调用 new MyThread.start()方法启动一个线程
2)实现Runnable接口,重写run()方法

class MyThread1 implements Runnable{
    @Override
    public void run() {
        System.out.println("implments Runnable to create a thread");
    }
}

仍然要调用start()方法启动一个线程
3)匿名线程(通过匿名类实现)

new Thread(){
	public void run(){
	
	}
}.start();

Thread thread = new Thread(){
	public void run(){
	
	}
};
thread.start();

不是调用run()方法去启动线程,因为Java是这么搞的,你把你想以多个线程展现的东西写在重写的run()里面,然后调用start()方法,如果进展顺利,就会调用一个start0()方法,这是一个Java的本地方法,这个方法用来启动线程,同时会将当前的线程加入到线程组中,线程就会进入Runnable状态。
源码:

public synchronized void start() {
        /*
        threadStatus,默认值 0,第一次启动后,就不是0了,所以说,一个线程只能被启动一次
        */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();
        group.add(this);//加入到一个线程组,进入到就绪状态
        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                //如果start0()失败,就把这个进程加入到这里面
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
3、线程的生命周期及线程状态之间的转换

又到了我灵魂画手展现威力的时刻了!!!哈哈哈!
Java中进程的生命周期与操作系统的中略有区别
Java:
1)New状态:创建一个线程对象
2)Runnabled状态:就绪状态,只要获得系统资源,就有执行的可能。
3)Running状态:执行状态
4)Blocked状态:阻塞状态,等待监视器锁/调用Object.wait方法
5)Waiting状态:同阻塞状态,Object.wait/thread.join/LockSupport.park()
6)Timed_Waiting状态:限时等待
7)Terminated状态:终止状态
相信学过操作系统的同学都有能力画出来,对于线程状态的转换也不难:⬇
与线程的第一次见面
完美(此处应有手势)。

操作系统:
New:新建状态
Runnabled:就绪状态
Blocked:阻塞状态
Running:执行状态
Dead:终止状态

4、线程的一些常用方法

1)不用多说,当然是启动线程,
start()方法。
2)sleep()方法,让线程睡一会儿。
sleep(long millis),单位是毫秒,想多睡一会儿的话就稍有不便。
所以这里有一个使用更方便的枚举类,TimeUnit

TimeUnit.NANOSECONDS.sleep(纳秒);
TimeUnit.MICROSECONDS.sleep(微秒);
TimeUnit.MILLISECONDS.sleep(毫秒);
TimeUnit.SECONDS.sleep(忘记了);
TimeUnit.MINUTES.sleep(不认识);
TimeUnit.HOURS.sleep(???);
TimeUnit.DAYS.sleep(-。-);
TimeUnit.MONTH.sleep(月!!!);//哦,不好意思,没有这个

另一个让线程飞一会儿的方法,yield()方法
它的使用跟sleep方法差不多,有一点点的区别如下:
①sleep只会导致当前线程暂停,不会放弃cpu时间片
②yield放弃cpu一次时间片这个过程是不一定,如果cpu没有忽略这个提示,那才会导致线程切换
③yield方法会使得线程从Running切换到Runnable状态
④sleep,使用interrupt会捕获到中断信号,yield不会

3)线程中断
interrupt - 由Thread的一个对象调用,如果线程处于这个状态,将中断状态位置为true
那么,sleep()/join/wait()则会使得当前的线程进入阻塞状态->中断状态
线程处于阻塞状态时不能被打断,如果被打断就会抛出一个InterruptedException
isInterrupted - Thread.xxx 检测中断状态位是否为true

interrupted - Thread.xxx 判断当前线程是否被中断
如果当前线程被打断,第一次调用interrupted会返回true,然后立即擦出当前inteerrupt标识,
第二次包括以后调用都返回的是false

4)获取当前线程
public static Thread currentThread() 返回当前执行线程的引用
也就相当于得到当前线程的一个对象(总觉得这句话是多余的。。。)

5)join方法
线程A调用join方法,会使得线程A调用join方法时所在的线程B进入到等待队列,
此时必须等到线程A执行完或者到达给定的时间, 线程B才可以去执行

6)守护线程
如果在主程序中存在非守护线程,会导致main线程结束,而子线程依旧在执行,JVM进程也将不会退出。
守护线程的意思就是,当主线程结束,它的守护线程也就随之而亡。
这么说也不太对,当线程B成为了A的守护线程,只要A结束,B也就跟着结束了,反之B结束,一般情况下,A的结束与否和B无关;当B不是A的守护线程,只要它们两个没得关系,who care who?

相关标签: Java 基础知识