进程
http://blog.csdn.net/eckotan/article/details/46854507
几乎所有操作系统都支持==进程==的概念,所有运行中的任务通常对应这一条进程(Process)。当一个程序进入内存(存储正在运行的程序和数据)运行时,就变成了一个进程。
注意区分并发性(concurrency)和并行性(parallel)这两个概念:
-
并行(parallel):指在同一时刻,有==多条==指令在==多个处理器==上==同时==执行。
-
并发(Concurrency): 指在同一时刻,只能有==一==条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程同时执行的效果.
- 线程(Thread)==也被称作 ==轻量级进程(Lightweight Process)==。线程(Thread)是进程(Process)的执行单元。就像进程在操作系统中的地位一样,线程在程序中是独立的、并发的执行流。当进程被初始化之后,主线程就被创建了。对于大多数的应用程序来说,通常仅要求有一个主线程,但我们也可以在该进程内创建多条顺序执行流,这些顺序执行流就是Thread,每条Thread也是互相独立的.
- 线程的划分尺度小于进程,使得多线程程序的==并发性高==。进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大的提高了程序的效率。
- 系统创建进程必须为该进程分配独立的内存空间,并分配大量相关资源,但创建一个线程则简单的得多.
-
java使用Thread类代表线程,所有的线程都必须是Thread类或其子类。
每条线程的作用是:==完成一定的任务,实际上就是执行一段程序流。==*java使用
run
方法来封装这样一段程序流,run
方法也被成为==线程执行体==* -
-
定义Thread类的子类,重写该类
run
方法。==run方法的方法体代表了该线程需要完成的任务==(想要它做什么,就往run方法里面写什么)。 -
创建线程对象
-
用线程对象的start方法来启动线程。
package thread.createThread; public class FirstThread extends Thread { private int i; public void run() { for (; i < 20; i++) { // shiyong getName()方法来返回当前线程的名字 System.out.println(this.getName() + " " + i); } } public static void main(String[] args) { for (int i = 0; i < 20; i++) { // 使用Thread类的静态方法 currentThread() 来获取当前线程 System.out.println(Thread.currentThread().getName() + " " + i); if (i == 10) { new FirstThread().start(); } if (i == 15) { new FirstThread().start(); } } } }
-
上面的程序只显式的启动了两条线程,但实际上有==3条线程==,因为还有包含main方法的主线程。主线程的线程体不是有run方法确定的,而是由main方法的方法体来确定。
-
上面还用到了线程类的两个方法:
-
Thread.currentThread()
:返回当前正在执行的线程对象 -
getName()
:返回调用该方法的线程的名字。程序可以通过
setName(String name)
方法来为线程设置名字。默认情况下下主线程的名字为main,用户启动的多条线程名字依次为Thread-0, Thread-1…
-
-
上面程序Thread-0和Thread-1输出的i并不连续,这是因为i是实例属性,程序每次创建线程对象都需要创建一个FirstThread对象,Thread-0和Thread-1不能共享i。(但如果把i设成static就可以)
使用继承Thread类的方法来创建线程,多条线程之间无法共享实例变量
-
-
实现Runnable接口创建线程类
步骤:
-
定义实现Runnable接口的类,重写
run
方法public class SecondThread implements Runnable
- 1
-
创建Runnable实现类的对象,并以此作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象。
SecondThread st = new SecondThread(); new Thread(st, "NewThread");
- 1
- 2
- 3
Runnable对象仅仅作为Thread对象的Target(在创建Thread对象时作为参数传进构造方法,Runnale实现类里包含的
run
方法仅仅作为线程执行体。==而实际的线程对象依然是Thread类的实例,只是该Thread线程负责执行其Target的run
方法而已==) -
调用线程对象的
start
方法来启动该线程
例:
package thread.createThread; public class SecondThread implements Runnable { private int i; public void run() { for (; i < 20; i++) { System.out.println(Thread.currentThread().getName() + " " + i); } } public static void main(String[] args) { for (int i = 0; i < 20; i++) { System.out.println(Thread.currentThread().getName() + " " + i); SecondThread st = new SecondThread(); if (i == 10) { new Thread(st, "newThread1").start(); } if (i == 15) { new Thread(st, "newThread2").start(); } } } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 这时两个线程的i变量是连续的,因为程序所创建的Runnable对象只是线程的target,而多条线程可以共享同一个target,也就是说可以共享同一个target的所有实例变量。
-
- 使用同步监视器的目的是:防止两条线程对同一个共享资源进行并发访问,因此==通常用可能被并发访问的共享资源当同步监视器==(比如上面的Account对象)
-
使用同步监视器的逻辑是:
==加锁 ——> 修改 ——> 修改完成 ——> 释放锁==
- 同步方法就是使用
synchronized
关键字来修饰某个方法,该方法称为同步方法 -
另一种线程同步的机制:它通过显式定义同步锁对象来实现同步,在这种机制下,同步锁应该使用Lock对象更适合。
Lock提供了比
synchronized
方法和代码块更广泛的锁定操作,Lock对象实现允许更灵活的结构,可以具有差别很大的属性,并可以支持多个相关的Condition对象。Lock是控制多个线程对共享资源进行访问的工具。通常,锁提供了对共享资源的独占访问,每次==只能有一个线程==对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。不过,某些锁可能允许对共享资源进行并发访问,比如
在实现线程安全的控制中,通常用ReadWriteLock
。ReentrantLock
,使用该Lock对象可以显式的加锁、释放锁 -
线程组(ThreadGroup)
-
java使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,对线程组的控制相当于同时控制这一批线程。
-
用户创建的所有线程都属于==指定线程组==。如果程序没有显式的制定线程属于哪个线程组,那么该线程属于默认线程组。默认情况下,子线程和创建它的父线程处于同一个线程组内:例如A创建了B并且没有指定B的线程组,那么B属于线程A所在的线程组
-
一旦某个线程加入了制定线程组之后,该线程将一直处于该线程组,知道该线程死亡,线程运行中途==不能改变它所属的线程组==。因此Thread类并没与提供setThreadGroup这样一个方法来改变线程所属的线程组,但是提供了一个getThreadGroup方法来返回该线程所属的线程组。
-
Thread类提供下面几个构造器来设置新家的线程属于哪个线程组:
-
Thread(ThreadGroup group, Runnable target)
-
Thread(ThreadGroup group, Runnnale target, String name)
Thread(ThreadGroup group, String name)
-
-
ThreadGroup类的构造方法:
-
ThreadGroup(String name)
-
Thread(ThreadGroup parent, String name)
以指定的名字和父线程组来创建一个新线程组
上面两个构造方法创建线程组实例时都必须为其制定一个名字,也就是线程组总是有一个字符串名字,改名字可调用ThreadGroup的
getName()
得到,==但不允许改变线程组的名字!== -
-
常用方法:
-
int activeCount()
返回此线程组中活动线程的数目
-
interrupt()
中断此线程组的所有线程
-
isDaemon()
判断该线程是否是后台线程组
-
setDaemon(boolean daemon)
把该线程组设厂后台线程组。后天线程组的特征是:当后条线程组的最后一个线程执行结束或最后一个线程被销毁,那么后台线程组将自动销毁
-
setMaxPriority(int pri)
设置线程组的最高优先级
-
-
-
Callable是Runnable接口的增强版,Callable也提供了一个
call()
方法作为线程执行体,但它比run方法更加强大牛逼:-
call()
方法可以有返回值 -
call()
方法可以声明抛出异常
类似于Runnable的用法,我们可以提供一个Callable对象作为Thread的target,而
call()
方法作为该线程的线程执行体。问题是:Callable接口并不是Runnable接口的子接口,而Thread的构造方法里形参的类型是Runnable,所以Callable对象不能直接作为Thread的target;而且call方法还有一个返回值——但call方法不是直接调用,而是作为线程执行体被调用的。为了解决这几个问题,java提供了Future接口来代表Callable接口里call方法的返回值,并且为Future接口提供了一个FutureTask实现类,这个类实现了Future接口,也实现了Runnable接口(Adapter模式),这样就可以作为Thread类的target了。 -
-
Thread Pool(线程池)
系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互。在这种情况下,使用线程池可以很好的提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池。
==线程池在系统启动时就创建了大量空闲的线程,程序将一个Runnable对象传给线程池,线程池就会启动一条线程来执行该对象的run方法,当run方法执行结束之后,该线程不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个Runnable对象的run方法。==
使用线程池可以有效的控制系统中并发线程的数量,当系统中包含大量并发线程时会导致系统性能严重下降,甚至会导致JVM崩溃,而线程池的最大线程参数可以控制系统中并发线程不超过此数目。
java提供了一个Executors工厂类来生产线程池,包含下面几个==静态工厂方法==来创建线程池:
-
newCachedThreadPool()
创建一个具有缓存功能的线程池,系统根据需要创建线程,这线程会被缓存在线程池中
-
newFixedThreadPool(int nThreads)
创建一个可重用的、具有固定线程数的线程池
-
newSingleThreadExecutor()
创建一个只有单线程的线程池,相当于
newFixedThreadPool(int nThreads)
传入参数为1 -
newScheduledThreadPool(int corePoolSize)
-
创建具有固定线程数的线程池,可以在指定延迟后执行线程任务。
-
corePoolSize指池中所保存的线程数,即使线程是空闲的也被保存在线程池里
-
-
newSingleThreadScheduledExecutor()
创建只有固定线程的线程池,可以在指定延迟后执行线程任务。
前三个方法返回一个
ExecutorService
对象,该对象代表一个线程池,可以执行Runnable或Callable对象所代表的线程。ExecutorService
代表==尽快执行线程的线程池(只要池中有空闲线程就立即执行线程任务)==,程序只需要将一个Runnable或Callable对象提交给该线程池即可,该线程池就会尽快执行该任务(实现的细节对客户解耦)。 -
- ThreadLocal,是ThreadLocalVariable的意思。功能是:==为每一个使用该变量的线程都提供一个变量值的副本,使每一个线程都可以独立的改变自己的副本,而不会和其他线程的副本发生冲突。==从每一个线程的角度来看,好像每一个线程都==完全拥有该变量==
-
包装线程不安全的集合
ArrayList、LinkedList、HashSet、HashMap等都是==线程不安全==的,也就是有可能当多个线程向这些集合放入一个元素时,可能会破坏集合数据的完整性。
可以使用Collections类提供的静态方法来把这些集合包装成线程安全的集合:
-
<T> Collection<T> synchronizedCollection(Collection<T> c)
返回指定Collection对应的线程安全的Collection
-
static <T> List<T> synchronizedList(List<T> list)
返回指定List对应的线程安全的List
-
static <K, V> Map<K, V> synchronizedMap(Map<K, V> m)
返回指定Map对应的线程安全的Map
-
static <T> Set<T> synchronizedSet(Set<T> s)
返回指定Set对应的线程安全的set
-
static <K, V> SortedMap<K, V> synchronizedSortedMap(SortedMap<K, V> m)
例:
HashMap m = Collections.sychronizedMap(new HashMap());
-