1.一个任务通常就是一个程序,每个运行中的程序就是一个进程。
当一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行流就是一个线程。
多线程是扩展了多进程的概念,使得同一个进程可以同时并发处理多个任务。
一个进程可以拥有多个线程,一个线程必须有一个父进程。
2.线程是独立运行的,它并不知道进程中是否还有其他线程存在。
线程的执行时抢占式的,也就是说,当前运行的线程在任何时候都可能被挂起,以便让另外一个线程可以运行。
3.当操作系统创建一个进程时,必须为该进程分配独立的内存空间,并分配大量的相关资源,并且多个进程之间不能共享内存;但创建一个线程则简单的多。同一个线程中的进程都有共性------共享一个进程虚拟空间。线程共享的环境包括:进程代码段、进程的公有数据。
sleep(): 强迫一个线程睡眠N毫秒。
isAlive(): 判断一个线程是否存活。
join(): 等待线程终止。
activeCount(): 程序中活跃的线程数。
enumerate(): 枚举程序中的线程。
currentThread(): 得到当前线程。
isDaemon(): 一个线程是否为守护线程。
setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
setName(): 为线程设置一个名称。
wait(): 强迫一个线程等待。
notify(): 通知一个线程继续运行。
setPriority(): 设置一个线程的优先级。
Thread.yield() 方法的作用:暂停当前正在执行的线程,并执行其他线程。(可能没有效果)yield()让当前正在运行的线程回到可运行状态,以允许具有相同优先级的其他线程获得运行的机会。因此,使用yield()的目的是让具有相同优先级的线程之间能够适当的轮换执行。但是,实际中无法保证yield()达到让步的目的,因为,让步的线程可能被线程调度程序再次选中。结论:大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。
定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
创建Thread子类的实例,即创建了线程对象。
调用线程对象的start()方法来启动该线程。
可以通过Thread.currentThread()方法获取当前正在运行的线程对象, 这种方式可以直接调用getName()方法获取线程名。
定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
调用线程对象的start()方法来启动该线程。
示例:
new Thread(new Runnable());
new Thread(new Runnable(),"新线程");
值得一提的是,实现Runnable接口时,获取当前线程名,必须使用Thread.currentThread().getName();
创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
使用FutureTask对象作为Thread对象的target创建并启动新线程。
调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
线程通常都有五种状态,创建、就绪、运行、阻塞和死亡。
①. newFixedThreadPool(int nThreads)
创建一个固定长度的线程池,每当提交一个任务就创建一个线程,直到达到线程池的最大数量,这时线程规模将不再变化,当线程发生未预期的错误而结束时,线程池会补充一个新的线程。
②. newCachedThreadPool()
创建一个可缓存的线程池,如果线程池的规模超过了处理需求,将自动回收空闲线程,而当需求增加时,则可以自动添加新线程,线程池的规模不存在任何限制。
③. newSingleThreadExecutor()
这是一个单线程的Executor,它创建单个工作线程来执行任务,如果这个线程异常结束,会创建一个新的来替代它;它的特点是能确保依照任务在队列中的顺序来串行执行。
④. newScheduledThreadPool(int corePoolSize)
创建了一个固定长度的线程池,而且以延迟或定时的方式来执行任务,类似于Timer。