一.什么是多线程
在学习多进程之前得先明白两个概念:
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1--n个线程,进程是资源分配的最小单位;
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小,线程是cpu调度的最小单位;
线程和进程一样分为五个阶段:创建、就绪、运行、阻塞、终止。
多进程是指操作系统能同时运行多个任务(程序)。
多线程是指在同一程序中有多个顺序流在执行。
多线程是多任务的一种特别的形式,但多线程使用了更小的资源开销。
这里定义和线程相关的另一个术语 - 进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个 进程一直运行,直到所有的非守护线程都结束运行后才能结束。多线程能满足程序员编写高效率的程序来达到充分利用 CPU 的目的。
二.怎样开启多线程
在java中要想实现多线程,有两种手段,一种是继承Thread类,另外一种是实现Runable接口。
实现Runable接口:
实现 Runnable,一个类只需要执行一个方法调用 run(),在创建一个实现 Runnable 接口的类之后,你可以在类中实例化一个线程对象,新线程创建之后,你调用它 的 start() 方法它才会运行。
继承Thread类:
创建一个线程的第二种方法是创建一个新的类,该类继承 Thread 类,然后创建一个该类的实例。继承类必须重写 run() 方法,该方法是新线程的入口点。它也必须 调用 start() 方法才能执行。
三、Thread和Runnable的区别
如果一个类继承Thread,则不适合资源共享。但是如果实现了Runable接口的话,则很容易的实现资源共享。
总结:
实现Runnable接口比继承Thread类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
四.线程状态的转换
创建:新创建了一个线程对象;
就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。该状态的线程位于可运行线程池中,变得可运行,等待获 CPU的使用权。
运行状态(Running):就绪状态的线程获取了CPU,执行程序代码。
阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、 或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期。
五.常用方法
sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)
join():指等待t线程终止。
yield():暂停当前正在执行的线程对象,并执行其他线程,在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果
sleep()和yield()的区别):sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,因此休眠时间结束后会自动恢复。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。
sleep()睡眠时,保持对象锁,仍然占有该锁;
wait()睡眠时,释放对象锁。
notify(): 通知一个线程继续运行,获得锁标记。
六.线程安全
一般理解为线程同步,线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏,线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个 特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法,当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻 塞,死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
保持线程同步的方法一般就是加锁,如synchronized关键字。synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步 语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。只要一个线程访问了其中 的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法。
原文地址:https://www.cnblogs.com/zxfjava/p/9164905.html