多线程
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。
1.多线程概述
计算机的操作系统采用多任务和分时设计,多任务是指在一个操作系统中可以同时运行多个程序。每个程序对应一个进程,每个进程也可以产生多个线程。
1.进程
(Process)是程序的一次动态执行过程,它对应了从代码加载,执行到执行完毕的一个完整过程,这个过程也是进程从产生,发展至消亡的过程。计算机系统中的多个进程轮流使用CPU资源,或者共享操作系统的 其他资源。
进程是系统运行的基本单位。
每一个进程都有自己独立的一块内存空间,一组系统资源。
每一个进程的内部数据和状态都是完全独立的。
在操作系统中可以有多个进程。
2.线程
线程是进程中执行运算的最小单位,可以完成一个独立的顺序控制流程。
每个进程中,必须至少建立一个线程(这个线程称为主线程)来作为这个程序运行的入口点。如果一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”。
3.多线程的好处
1)充分利用CPU的资源。
2)简化编程模型,一个既长又复杂的进程可以考虑分为多个线程
3)带来良好的用户体验,CPU的效率提高,降低了用户等待的几率。
4.主线程
在java程序启动时,一个线程立刻运行,该线程通常称为程序的主线程。Java中的public static void main()方法是主线程的入口,每个进程至少有一个主线程。它是程序开始就执行的。主线程的重要性:
a) 它是产生其他子线程的线程
b) 通常它必须最后完成执行,因为它执行各种关闭动作。
2.实现多线程
1.继承Thread类创建线程
主要的语法:
Thread thread=new Thread( );
Thread类常用的方法 |
|
构造方法 |
说明 |
Thread() |
分配新的Thread对象 |
Thread(Runnable target) |
分配新的Thread对象,target为run()方法被调用的对象 |
Thread(Runnabel target,String name) |
分配新的Thread对象,target为run()方法被调用的对象,name为新线程的名称 |
Void run() |
执行任务操作的方法 |
Void start() |
使该线程开始执行,java虚拟机调用该线程的run方法 |
Void sleep(long millis) |
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行) |
String getName() |
返回线程的名称 |
int getPriority() |
返回线程的优先级 |
Void setPriority() |
更改线程的优先级(1-10级,提高执行的概率,不是绝对) |
Static Thread currentThread() |
返回当前正在执行的线程对象的引用 |
Boolean isAlive() |
测试线程是否处于活动状态 |
Void join() |
等待该线程终止(强行插队) |
Void interrupt() |
中断线程 |
Void yield() |
暂停当前正在执行的线程对象,并执行其他线程(礼让,提高概率不是绝对) |
2.实现Runnable接口创建线程
主要的语法:
- 实现Runnable接口的类及run()方法的重写
- MyRunnable runnable=new MyRunnable(实现Runnable接口的类);
Thread thread=new Thread(runnable);
3.start()方法和run()方法的区别
调用start()方法表示该线程处于启动状态,等待操作系统分配资源执行run()方法中的代码,在多个线程处于启动状态时,线程是交替执行的,从而实现多线程。
直接调用run()方法和调用实例方法没有区别,程序是按顺序执行的,属于单线程执行模式。
3.线程的状态
在java中,线程通常有五种状态,创建,就绪,运行、阻塞和死亡状态。
第一是创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。
第二是就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。
第三是运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。
第四是阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。
第五是死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪。
4.线程的调度
1.线程的优先级
多个线程处于运行状态时,它们需要排队等待CPU的资源,每个线程会自动获取一个线程的优先级(Priority)。一般情况下,优先级越高的线程获得CPU资源的概率较大,但不是绝对的,至少提高概率而已。
线程的优先级用1~10表示,共有10个等级,1为最低,10为最高,系统默认为5.这些优先级对应一个Thread类的公用静态常量。
2.线程的休眠
语法: Thread.sleep(millis毫秒)
Sleep方法会让当前线程休眠()停止执行millis毫秒,线程由运行状态进入不可运行状态,睡眠时间过后线程会再次进入可运行状态。
调用Sleep()方法需要处理interruptedException异常。(try/catch)
3.线程的强制运行
语法:Thread(线程对象).join();
它有三个重载方法:
- Public final void join();
- Public final void join(long mills);
- Public final void join(long mills,int nanos);
使当前线程暂停执行,强制执行插入的join对象的线程,等到线程执行完毕后,再执行当前线程。
Jion()方法同样需要处理interruptedException异常。(try/catch)
4.线程的礼让
Yield()方法可暂停当前线程执行,允许其他相同优先级的线程获得运行机会,该线程仍处于就绪状态,不会转为阻塞状态。
线程的礼让只是提供一种可能,但是不能保证一定会实现礼让,因为礼让的线程也处于就绪状态,还有可能被线程调度程序再次选中。
5.线程的同步
1.多线程共享数据引发的问题
由于多个线程并发执行操作同一共享资源时,将带来数据不安全的问题。
比如创建多个线程模拟网络订票,会出现多个线程抢到同一张的票的问题,从而造成了同一共享资源数据的不安全。
2.线程同步的实现
1.同步的方法
使用synchronized修饰的方法控制对类成员变量的访问。每个类实例对应一把锁,方法一旦执行就独占该锁,直到方法返回时,该锁才释放,此后被阻塞的线程方能获得该锁,重新进入执行状态。这种机制确保了同一时刻对应一个实例,从而有效避免了类成员变量的访问冲突。
实现的语法:
访问修饰符 synchronized 返回类型 方法名(参数列表){//方法体}
或: synchronized 访问修饰符 返回类型 方法名(参数列表){//方法体}
在语法中synchronized是同步关键字
同步方法的缺陷:
如果一个运行时间比较长的方法声明成Synchronized将会影响效率。如果将run()方法声明synchronized,由于在线程的整个生命周期内它一直在运行,因此就可能导致run()会执行很长的时间,那么其他的线程就需要一直等到run()方法结束了才能执行。
解决方法:
最好不要run()方法中声明
2.同步代码块
使用synchronized关键字修饰的代码块,称为同步代码块。语法如下:
Synchronized(syncObject){//需要同步的代码块}
Synchronized块中的代码必须获得对象syncObject的锁才能执行,具体机制与同步方法一致,由于可以针对任意代码块,并且可以任意指定上锁的对象,因此灵活性更高。
3.线程安全的类型
1.线程的安全
若在进程中有多个线程,而当这些线程同时运行,每次运行结果和单线程运行的结果时一样的,而且其他的变量值也和预期的是一样的,这样线程就是安全的。
2.线程安全的类型概念
一个类在被多个线程访问时,不管运行时环境执行这些线程由什么养的时序安排,它必须是以固定的,一致的顺序执行。这样的类型称为线程安全的类型。
3.线程安全和非安全
线程安全 |
方法同步 |
效率低 |
多线程共享资源 |
非线程安全 |
方法不同步 |
效率高 |
单线程 |
4.线程安全类型的实例
- shtable是线程安全的,不允许出现空值,方法同步;HashMap是非线程安全,允许出现空值,重速度清安全。两者都实现了Map接口,Hashtable继承了Dictionary类,HashMap继承AbstracyMap类。
- StringBuffer和StringBuilder都可以用来存储字符串变量,是可变的对象。区别在于StringBuffer是线程安全的,而StringBuilder是非线程安全的。
以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章