进程与线程
进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。(按照这个解释可以理解为进程是一个程序在某个数据集合中的执行过程,它是操作系统动态执行的基本单元)
线程:是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
使用多线程就是将一个进程中的多个线程并发(将CPU的运行时间划分为多个时间段,然后通过调度算法,将时间段分给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状态)的执行,同样,不同进程中的线程也能并发执行,这样可以充分发挥CPU的效率;
使用Java实现多线程
继承Thread类
Thread类实现了Runnable接口,可以通过继承Thead类的方式实现多线程,但是由于java没有多继承,如果使用这种方式就不能再继承别的类。
package StartingThreads_1;
/**
* Starting Threads with extends
* @author Z.B. Celik <celik.berkay@gmail.com>
*/
class Runner extends Thread
{
@Override
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println("Hello: " + i + " Thread: "
+ Thread.currentThread().getName());
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
public class ApplicationExtends
{
public static void main(String[] args)
{
Runner runner1 = new Runner();
runner1.start();
Runner runner2 = new Runner();
runner2.start();
}
}
start()方法的调用后并不是立即执行多线程代码,而是通知线程规划器此时该线程已准备就绪,变为可运行态(Runnable),等待调用线程的run()方法运行线程,如果直接调用线程的run方法,将由程序的main主线程调用该方法,那么就程序将变成顺序执行。
运行程序同时可以看出程序的运行时乱序的,实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。
JVM可以分为四个部分:类加载器(Class Loader),执行引擎(Execution Engine),本地接口(Native Interface),运行数据区(Runtime data area)。
实现Runnable接口
由于Thread类是实现了Runnable接口的,所以可以直接实现Runnable接口来实现多线程。
package StartingThreads_1;
/**
* Starting Threads using Runnable Interface
* @author Z.B. Celik <celik.berkay@gmail.com>
*/
class RunnerRunnable implements Runnable
{
@Override
public void run()
{
for (int i = 0; i < 5; i++)
{
System.out.println("Hello: " + i + " Thread: "
+ Thread.currentThread().getName());
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}
public class ApplicationRunnable
{
public static void main(String[] args)
{
Thread thread1 = new Thread(new RunnerRunnable());
Thread thread2 = new Thread(new RunnerRunnable());
thread1.start();
thread2.start();
}
}
在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnable target) 构造出对象,然后调用Thread对象的start()方法来运行多线程代码。
实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。
线程的几种状态
- 新创建:当new操作符创建一个线程,这意味着它当前处于新创建状态程序还没有开始运行线程中的代码;
- 可运行:一旦调用start方法,线程处于可运行状态,一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间;
- 被阻塞:当一个线程企图获取内部的对象锁,而该锁被其他线程持有,则该线程进入阻塞状态,当所有其他线程释放该锁,并且线程调度器允许本线程持有它的时候,该线程变成非阻塞状态;
- 等待状态:当线程等待另一个线程通知调度器一个条件时,如调用wait和join方法,或者等待锁的condition时;
- 计时等待:有几个方法有一个超时参数,当被调用时,这一状态会一直保持到超时期满或者接收到适当的通知,带有超时参数的方法是:Thread.sleep\Object.wait\Thread.join\Lock.tryLock\Condition.await
- 被终止:线程有两个原因会被终止:因为run方法正常退出而自然死亡,因为一个没有捕获的异常终止run方法而意外死亡;
线程属性
线程优先级
每一个线程都有一个优先级,默认情况下一个线程继承其父线程的优先级,每当线程调度器有机会选择新线程时,它会优先选择高优先级的线程执行;
JDK中线程的优先级分为1-10级,从MIN_PRIORITY(1)到MAX_PRIORITY(10),默认情况下,线程的优先级为NORM_PRIORITY(5)可以使用线程的setPriority函数设置线程的优先级
注:线程的优先级高度依赖于系统,当虚拟机依赖于宿主主机平台线程实现机制时,Java线程的优先级被映射到宿主主机平台的优先级上,优先级个数或许更多,或许更少;如果高优先级的线程没有进入活动状态,低优先级线程可能永远不会执行。
守护线程
为其它线程提供服务的线程,当进程中不存在非守护线程,守护线程将自动销毁,最常见的守护线程如计时线程、垃圾回收线程。可以是有setDaemon(true)将一个线程设置为守护线程,,守护线程应该永远不去访问固有资源比如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
未捕获异常
线程的run方法不能抛出任何被检测的异常,不被检测的异常会被传递到一个用于未捕获异常的处理器,该处理器必须是一个实现Thread.UncaughtExceptionHandler接口的类,这个接口只有一个方法void uncaughtException(Thread t, Throwable e),可以使用setUncaughtExceptionHandler为所有线程安装一个默认的处理器。