参考:
1.线程与进程的区别
定义:
进程
是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程
是系统进行资源分配和调度的一个独立单位.线程
是进程的一个实体,是CPU调度和分派的基本单位,它是比进程
更小的能独立运行的基本单位.线程
自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程
的其他的线程
共享进程所拥有的全部资源。
进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
- 1 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
- 2 线程的划分尺度小于进程,使得多线程程序的并发性高。
- 3 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
- 4 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 5 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。
这些是比较字面的描述,更加深刻的认识可以参考:线程-进程
2.Java中线程的几种状态
在Java中,线程通常都有五种状态,创建、就绪、运行、阻塞和死亡,可见下图:
下面是各个状态的简单介绍:
1.新建状态(new): 新创建了一个线程对象。
3.线程调度的相关知识
线程的调度
- static int MAX_PRIORITY
- 线程可以具有的最高优先级,取值为10。
- static int MIN_PRIORITY
- 线程可以具有的最低优先级,取值为1。
- static int NORM_PRIORITY
- 分配给线程的默认优先级,取值为5。
4.Java中线程调度的常用API
①sleep(long millis): 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)②join():指等待t线程终止。
join是Thread类的一个方法,启动线程后直接调用,即join()的作用是:“等待该线程终止”,这里需要理解的就是该线程是指的主线程等待子线程的终止。也就是在子线程调用了join()方法后面的代码,只有等到子线程结束了才能执行。
- Thread t = new AThread(); t.start(); t.join();
③yield(): 暂停当前正在执行的线程对象,并执行其他线程。
yield()与sleep()方法类似,只是不能指定暂停多久,并且yield()方法只能让同优先级的方法获取运行的机会,需要注意的是,yield()只是让线程由运行状态转到就绪状态,但是可能并未有效果(该线程再次拿到cpu使用权)。
yield()与sleep()的区别:sleep 方法使当前运行中的线程睡眼一段时间,进入不可运行状态,这个时间长短可以由用户设定,允许较低优先级的线程获得运行机会;而yield方法使当前线程让出 CPU 占有权,让出的时间是不可设定的,并且只有相同优先级的线程能够获取cpu占有权,较低优先级线程只能等待所有较高优先级的线程运行结束,才有机会运行。
④setPriority(): 更改线程的优先级。
MIN_PRIORITY = 1
NORM_PRIORITY = 5
MAX_PRIORITY = 10
用法:
- Thread4 t1 = new Thread4("t1");
- Thread4 t2 = new Thread4("t2");
- t1.setPriority(Thread.MAX_PRIORITY);
- t2.setPriority(Thread.MIN_PRIORITY);
⑤interrupt():不要以为它是中断某个线程!它只是线线程发送一个中断信号,让线程在无限等待时(如死锁时)能抛出抛出,从而结束线程,但是如果你吃掉了这个异常,那么这个线程还是不会中断的!
⑥wait(),notify(),synchronized()
此三个方法是任何对象都拥有的同步工具,
monitor:
首先就要明确monitor的概念,Java中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在synchronized 范围内,监视器发挥作用。
wait/notify必须存在于synchronized块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着wait之后,其他线程可以进入同步块执行。
用法:
- synchronized单独使用:
- 代码块:如下,在多线程环境下,synchronized块中的方法获取了lock实例的monitor,如果实例相同,那么只有一个线程能执行该块内容
public class Thread1 implements Runnable { Object lock; public void run() { synchronized(lock){ ..do something } } }
- 直接用于方法: 相当于上面代码中用lock来锁定的效果,实际获取的是Thread1类的monitor。更进一步,如果修饰的是static方法,则锁定该类所有实例。
public class Thread1 implements Runnable { public synchronized void run() { ..do something } }
- 代码块:如下,在多线程环境下,synchronized块中的方法获取了lock实例的monitor,如果实例相同,那么只有一个线程能执行该块内容
synchronized, wait, notify结合:循环打印A,B:
- public class MythreadPrinter implements Runnable {
- private String name;
- private Object prev;
- private Object self;
- @Override
- public void run() {
- int count = 3;
- while (count>0){
- synchronized (prev){
- synchronized (self){
- System.out.println(name);
- count--;
- self.notify();
- }
- try {
- prev.wait();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- }
- public static void main(String[] args) throws Exception{
- Object a = new Object();
- Object b = new Object();
- MythreaPrinter pa = new MythreaPrinter("A",b,a);
- MythreaPrinter pb = new MythreaPrinter("B",a,b);
- new Thread(pa).start();
- Thread.sleep(100);
- new Thread(pb).start();
- Thread.sleep(100);
- }
- }
补充: volatile关键字
多线程的内存模型:main memory(主存)、working memory(线程栈),在处理数据时,线程会把值从主存load到本地栈,完成操作后再save回去(volatile关键词的作用:每次针对该变量的操作都激发一次load and save)。
针对多线程使用的变量如果不是volatile或者final修饰的,很有可能产生不可预知的结果(另一个线程修改了这个值,但是之后在某线程看到的是修改之前的值)。其实道理上讲同一实例的同一属性本身只有一个副本。但是多线程是会缓存值的,本质上,volatile就是不去缓存,直接取值。在线程安全的情况下加volatile会牺牲性能。
(7). 其他常用方法介绍:
sleep(): 强迫一个线程睡眠N毫秒。
isAlive(): 判断一个线程是否存活。
join(): 等待线程终止。
activeCount(): 程序中活跃的线程数。
enumerate(): 枚举程序中的线程。
currentThread(): 得到当前线程。
isDaemon(): 一个线程是否为守护线程。
setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
setName(): 为线程设置一个名称。
wait(): 强迫一个线程等待。
notify(): 通知一个线程继续运行。
setPriority(): 设置一个线程的优先级。