多线程与线程池
名词解释
- 进程(process):计算机已运行程序的实体。程序本身只是指令、数据及其组织形式的描述,进程才是程序(那些指令和数据)的真正运行实例。用户下达运行程序的命令后,就会产生进程。同一程序可产生多个进程(一对多关系),以允许同时有多位用户运行同一程序,却不会相冲突。进程是拥有资源的基本单位。进程需要一些资源才能完成工作,如CPU使用时间、存储器、文件以及I/O设备,且为依序逐一进行,也就是每个CPU核心任何时间内仅能运行一项进程。进程之间是资源隔离的,如果在进程间共享内容/消息传递代价较大。
- 线程:进程中负责程序执行的执行单元,线程是独立调度(CPU调度)和分派的基本单位,OS做调度时处理的是线程。一个进程中至少有一个线程,每个线程执行的都是进程代码中的某个片段。每个线程都拥有单独的栈内容来做存储本地数据。
- Java有没有多进程编程?否!java程序运行在JVM中,JVM本身就是java.exe运行起来,所以对OS而言,JVM是一个进程,其中无法进行多进程编程。
- 多线程:多条线程同时存在,例如写word时,一边输入一边自动保存,就需要两条线程来分别完成。关键技术:线程间通信,线程间共用资源。
- 线程池:一种线程使用模式。使用少量线程并让线程保持忙碌。以避免每个用户的请求都反复创建线程的开销。当线程池的线程刚创建时,让他们进入阻塞状态,等到任务来了唤醒即可。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。
有关线程你该知道的
创建线程的两种方式:
- 继承Thread类,扩展线程;
- 实现Runnable接口,java不支持多类继承,所以要继承其他类时调用Runnable接口更好。
具体代码及解释参考文首孙富生简书链接。
Thread类中的start()和run()方法区别
- 调用run()方法,用当前线程去执行一个普通方法,并没有创建新线程;
- 调用start()方法才会启动新线程,该方法会做一些准备工作:设置好这个线程的上下文,比如这个线程的栈(用于函数调用),线程的状态,这个线程的PC(Program Counter)等等一系列信息以后,这个线程才可以被调度,一旦被调度,就会执行那个run()方法了。
多线程
使用多线程的优缺点
优点:
- 适当的提高程序的执行效率(多个线程同时执行)。
- 适当的提高了资源利用率(CPU、内存等)。
缺点:
- 占用一定的内存空间。
- 线程越多CPU的调度开销越大。
- 程序的复杂度会上升。
多线程的技术点
synchronized关键字
同步由关键字synchronized来实现,所有加上synchronized的块语句和方法(成员方法/静态方法),在多线程访问的时候,同一时刻只能有一个线程能够访问。
同步问题解决办法:
1.将竞争资源标记为private;2.用synchronized关键字同步那些修改变量的代码
wait()、notify()、notifyAll()
这三个方法是 java.lang.Object 的 final native 方法,任何继承 java.lang.Object 的类都有这三个方法(不属于thread)。它们是Java语言提供的实现线程间阻塞和控制进程内调度的底层机制,平时我们会很少用到的。
- wait():
导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒,该方法只能在同步方法中调用。- notify():
随机选择一个在该对象上调用wait方法的线程,解除其阻塞状态,该方法只能在同步方法或同步块内部调用。- notifyAll():
解除所有那些在该对象上调用wait方法的线程的阻塞状态,同样该方法只能在同步方法或同步块内部调用。
调用这三个方法中任意一个,当前线程必须是锁的持有者,如果不是会抛出一个 IllegalMonitorStateException 异常。
wait() 与 Thread.sleep(long millis,int nanos)
- wait() 方法使实体所处线程暂停执行,从而使对象进入等待状态,直到被 notify() 方法通知或者 wait() 的等待的时间到。
- sleep() 方法使持有的线程暂停运行(仅能控制当前正在运行的线程),从而使线程进入休眠状态,直到用 interrupt 方法来打断他的休眠或者 sleep 的休眠的时间到。
wait()会释放同步锁,sleep()方法不会
volatile 关键字
- volatile 是一个特殊的修饰符,只有成员变量才能使用它。在Java并发程序缺少同步类的情况下,多线程对成员变量的操作对其它线程是透明的。
- volatile 变量可以保证下一个读取操作会在前一个写操作之后发生(使该 变量可被其他线程正确的读取)。线程都会直接从内存中读取该变量并且不缓存它。这就确保了线程读取到的变量是同内存中是一致的。
- valatile 不可用在final变量前
ThreadLocal 变量
ThreadLocal 是Java里一种特殊的变量。每个线程都有一个,ThreadLocal 就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了。如果为每个线程提供一个自己独有的变量拷贝,将大大提高效率。首先,通过复用减少了代价高昂的对象的创建个数。其次,你在没有使用高代价的同步或者不变性的情况下获得了线程安全。
join() 方法
join() 方法定义在 Thread 类中,所以调用者必须是一个线程,join() 方法主要是让调用该方法的 Thread 完成 run() 方法里面的东西后,再执行 join() 方法后面的代码(实现了顺序性,避免了并发执行)
Thread.yield() 方法
- 线程放弃运行,将CPU的控制权让出。
- 虽然让出了,但还有可能被系统的调度机制再次选中来运行。
未完待续 。。