相关阅读
【小家java】java5新特性(简述十大新特性) 重要一跃
【小家java】java6新特性(简述十大新特性) 鸡肋升级
【小家java】java7新特性(简述八大新特性) 不温不火
【小家java】java8新特性(简述十大新特性) 饱受赞誉
【小家java】java9新特性(简述十大新特性) 褒贬不一
【小家java】java10新特性(简述十大新特性) 小步迭代
【小家java】java11新特性(简述八大新特性) 首个重磅LTS版本
说到线程,很多人最直观的感受就是多线程。本章不讨论高并发、多线程之类的。返璞归真,咱们来讨论讨论线程这个东西到底是什么东西,着眼于线程本身,我们怎么玩?
为了了解线程,这里我不得不搬出来线程的概念了:
进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.(比如一个qq程序就是一个进程或者多个进程),系统进行资源分配的最小单位.。进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全
》》》》》》》》》》》》》》》》》》》
线程:程序执行流的最小单元(操作系统可识别的最小执行和调度单位)。进程是系统进行资源分配和调度的一个独立单位。也可以理解线程是一个程序里面不同的执行路径。是为了提高cpu的利用率而设计的。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据
》》》》》》》》》》》》》》》》》》》
协程(线程的线程):协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。
虽然协程属于比较热门的一个话题,但是java视乎反应较慢,几乎没有什么应用(实现Coroutine来创建协程),最早应该是python里用得稍微多一点。但对于面向应用的java来说,貌似作用不大,了解一下即可
线程是轻量级的进程,它们是共享在父进程拥有的资源下,每个线程在父进程的环境中顺序的独立的执行一个活动,每个CPU核心在同一时刻只能执行一个线程,尽管我们有时感觉自己的计算机同时开着多个任务,其实他们每个的执行都是走走停停的,CPU轮流给每个进程及线程分配时间。
线程的分类:用户线程和守护线程
用户线程:是用户创建的一般线程,如继承Thread类或实现Runnable接口等实现的线程。
守护线程:是为用户线程提供服务的线程,如JVM的垃圾回收、内存管理等线程。
守护线程和用户线程的区别:当一个用户线程结束后,JVM会检查系统中是否还存在其他用户线程,如果存在则按照正常的调用方法调用。但是如果只剩守护线程而没有用户线程的话,JVM就会终止(从始至终都没有理睬守护线程)。
任何线程都可以是守护线程或者用户线程,所有线程一开始都是用户线程。
涉及守护线程的方法有两个:setDaemon( )和 isDaemon()。
Thread.setDaemon(false/true)设置为用户线程/守护线程;如果不设置该属性,默认为false。
需要注意的是:setDaemon()方法仅仅在线程对象已经被创建但是还没有运行前才能被调用,否则会报错。
介绍Threa
线程从创建到最终的消亡,要经历若干个状态。一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)。这个给一幅图,好好理解一下线程的状态转换:
Thread类实现了Runnable接口,在Thread类中,有一些比较关键的属性,比如name是表示Thread的名字,可以通过Thread类的构造器中的参数来指定线程名字,priority表示线程的优先级(最大值为10,最小值为1,默认值为5),daemon表示线程是否是守护线程,target表示要执行的任务。下面介绍几个常用方法:
start方法:用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。
run方法:run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。
sleep方法:相当于让线程睡眠,交出CPU,让CPU去执行其他的任务。(但是有一点要非常注意,sleep方法不会释放锁,也就是说如果当前线程持有对某个对象的锁,则即使调用sleep方法,其他线程也无法访问这个对象)。当上面一个线程sleep,它不会释放锁,后面的线程都将会被阻塞。当线程睡眠时间满后,不一定会立即得到执行,因为此时可能CPU正在执行其他的任务。所以说调用sleep方法相当于让线程进入阻塞状态。
yield方法:它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。
join方法:join() join(long millis) 假如在main线程中,调用thread.join方法,则main方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的时间。