目录
概述
进程:是一个正在执行中的程序,每一个进程的执行都有一个执行顺序,该执行顺序是一个控制单元。是线程的容器。资源调度、分配和管理的最小单位。
线程:是进程中的一个独立的控制单元、运行基本单位(CPU调度的最小单位),线程控制着进程的执行。
线程系列
每一个进程中至少有一个线程,在java中主(main)线程就是一条线程。Java程序每次运行不止有一个main线程,还有如垃圾回收机制的线程
如何实现线程?
- 根据JavaAPI中所写,有Thread类。
public class Thread extends Object implements Runnable
创建一个新的执行线程有两种方法。
一、将一个类声明为Thread的子类,这个子类应该重写run类的方法Thread
二、创建一个线程是声明实现类Runnable接口。那个类然后实现了run方法。然后可以分配类的实例,在创建Thread时作为参数传递,并启动。
//方法一
class PrimeThread extends Thread{
public void run(){
//需要线程执行的代码
}
}
public static void main(String[] args){
PrimeThread p = new PrimeTherad();
p.start();
}
//方法二
class PrimeRun implements Runnable{
public void run(){
//需要线程执行的代码
}
}
public static void main(String[] args){
PrimeThread p = new PrimeTherad();
Thread t = new Thread(p);
t.start();
//简写 new Thread(new PrimeThread()).start();
}
- 为什么要覆盖run()方法?
Thread类用于描述线程。该类就定义了一个功能,用于存储要线程运行的代码,该存储功能就是run()方法。可以看出main()方法是存放主线程代码的位置。
线程的使用
随机性:
谁抢到cpu谁执行(多核除外),至于执行多长时间是cpu说了算。一般不会刻意的执行完一条线程再执行另一条。生命周期:
两种方法使用的区别
在上述自定义线程中我们可以知道一共有两种方法可以实现多线程。
那么这两种有什么区别呢?
一、避免单继承的局限性:
二、代码存放的位置不同:
继承Thread,线程代码存放在Thread子类run方法中。
实现Runnable,线程代码存放在接口的子类的run方法中。
因为Runnable只有一个抽象方法run()。
多线程的安全问题
问题原因
当多条语句操作同一个线程共享数据时,一个线程对多条语句只执行一部分,还没有执行完,另一个线程参与进行执行,导致共享数据的错误。解决办法
对多条操作共享数据的语句,只能让一个线程执行完在执行过程中不可以参与执行。Java提供了同步代码块——synchronized,实现了其原子性。
所以推荐使用Runnable接口实现多线程
停止线程
一般来说开启多线程运行的运行代码是循环结构,所以只要控制循环,就可以让run方法运行结束,线程就结束了,这种是最好的。
- interrupt()
public void interrupt()
中断这个线程。
一般使用方法:用其他线程去打断处于阻塞(冻结)状态的线程,强制使其恢复带运行状态。这时该被恢复的线程会抛出InterruptException异常,我们就可以在这个catch里面进行代码操作。
- setDaemon()
public final void setDaemon(boolean on)
将此线程标记为daemon线程或用户线程。当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。
线程启动前必须调用此方法。
当线程开启守护线程后与普通线程共用CPU资源,在这些过程中是没有区别的。
当所有非守护线程结束后,JVM退出,守护线程会自动结束。
- join()
public final void join() throws InterruptedException
等待这个线程死亡。
当前线程调用这个方法时,表该线程需要当前CPU的执行权(抢夺性的),拥有者CPU执行权的线程会释放执行权。一般用于临时加入线程。
- yield
public static void yield()对调度程序的一个暗示,即当前线程愿意分享当前使用的CPU。 调度程序可以自由地忽略这个提示。
以改善否则会过度利用CPU的线程之间的相对进度。 其使用应与详细的分析和基准相结合,以确保其具有预期的效果。
临时释放,稍微缓解线程的执行频率以达到线程能平均占有CPU的效果。
其他常用方法
- setPriority
public final void setPriority(int newPriority)更改此线程的优先级。
默认的优先级是5,(1-10),优先级越高执行频率越高,哪怕是10也不存在一直完全占有CPU。
默认参数(MIN_PRIORITY,NORM_PRIORITY,MAX_PRIORITY)
- toString
public String toString() 返回此线程的字符串表示,包括线程的名称,优先级和线程组。
Thread 类中覆盖了Object 中toString 方法建立了特有的字符串表现形式。
- getName
public final String getName()返回此线程的名称。
使用线程的快速方式
//一
new Thread(){
public void run(){
//线程执行代码
}
}.start();
//二
Thread t = new Runnable(){
public void run(){
//线程执行代码
}
}
new Thread(t).start();
锁系列
锁是用于通过多个线程控制对共享资源的访问的工具。通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。
如何实现锁?
特点:
对象如同锁,持有锁的线程可以在同步中执行。需要用一个对象锁。 弊端:多个线程需要判断锁,较销毁资源。同步前提:
1、两个或者两个线程以上的时候。
2、多个线程需要使用同一个锁。同步表现形式
一、同步代码块。 二、同步函数。
synchronized(对象){
//需要被同步的代码
}
public synchronized void Demo(){
//需要被同步的代码
}
是哪一个锁?
实现一,锁是括号中传入的对象。
实现二,锁是当前的类是this。
因为函数需要被对象调用,那么函数都有一个所属引用。就是this。静态函数是哪一个锁?
public static synchronized void Demo(){
//需要被同步的代码
}
静态进内存时,没有本类对象,所以没有this。 是由类调用的,所以是该类对应的字节码文件对象 —— 类名.class
死锁
- 死锁的形成
当两个或两个以上的进程或线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象。
synchronized(对象1){
//拥有对象1锁的线程执行到着的时候就会去获取对象2的锁,
synchronized(对象2){
//需要被同步的代码
}
}
synchronized(对象2){
//拥有对象2锁的线程执行到着的时候就会去获取对象1的锁,
synchronized(对象1){
//需要被同步的代码
}
}
线程间的通讯
在多线程的程序中线程之间的通讯是非常重要的
Object-wait and notify and notifyAll
该方法只能由作为该对象的监听器的所有者的线程调用。
- wait()
public final void wait()
throws InterruptedException
导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法。
当前的线程必须拥有该对象的监听器。 该线程释放此监听器的所有权,并等待另 一个线程通知等待该对象监听器的线程通过调用notify方法或notifyAll方法 。
然后线程等待,直到它可以重新获得监听器的所有权并恢复执行。
注意点
一、我们可以知道wait() notify() notifyAll() 只能用于同步中。
二、wait() 是让持有当前锁上的线程等待并交出锁监听器。
三、只能让另一个线程执行notify()或notifyAll()才能唤醒wait()中的线程。
四、在执行的时候可以用 锁对象.wait() 的方式执行。
五、一个锁就对应了一个Object。wait() 机制
在同步中,当一个线程执行 锁对象.wait() 的时候 这个线程将会回到线程池中按顺序进行等待唤醒。意思就是说当有第一个线程被 wait() 的时候它回到线程池中排序为第一号,第二个被 wait() 回到线程池就是第二号,所以当另一个线程执行notify的时候首先被唤醒的就是第一号。为什么定义在Object类中?
因为这些方法在操作同步中线程时,都必须标识它们所操作的持有锁,只有同一个锁上的被等待的线程,可以被同一个 notify() 唤醒,不可以对不同锁中的线程进行唤醒。弊端
在多线程中哪个线程获得cpu是具有随机性的,所以如果我们要处理多线程之间的等待和唤醒的时候,就可能会遇到问题造成所有的线程都进行wait()状态。notify() 和 notifyAll() 区别
因为存在上述的弊端,所以就出现了notifyAll()它可以唤醒同一个锁上被 wait() 的所有线程。
Lock
Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。 它们允许更灵活的结构化,可能具有完全不同的属性,并且可以支持多个相关联的对象Condition(特点:可实现一个Lock对应多Condition) 。
- Condition
Condition因素将Object监听器方法(wait,notify和notifyAll )放入到不同的对象,以使每个对象具有多个等待集合的效果,通过将它们与任意的Lock实现结合使用。 Lock替换synchronized方法和语句的使用, Condition取代了Object监听器方法的使用。
一个Condition实例本质上绑定到一个锁。 要获得特定Condition实例的Condition实例,请使用其newCondition()方法。
Condition基本使用
void await() 导致当前线程等到发信号或 interrupted 。
signal() 唤醒一个等待线程。
signalAll() 唤醒所有等待线程。Lock基本使用
void lock() 获得锁。
Condition newCondition() 返回一个新Condition绑定到该实例Lock实例。
void unlock() 释放锁。注意点
当在不同范围内发生锁定和解锁时,必须注意确保在锁定时执行的所有代码由try-finally或try-catch保护,以确保在必要时释放锁定。用它特有的性质实现生产者-消费者模式
import java.util.concurrent.locks.*;
class ProductConsumerDemo{
public static void main(String[] args){
Rescure res = new Rescure();
new Thread(new Producer(res)).start();
new Thread(new Consumer(res)).start();
new Thread(new Producer(res)).start();
new Thread(new Consumer(res)).start();
}
}
class Rescure{
private int count = 1;
private String name;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name)throws InterruptedException{
lock.lock();
try{
while(flag)
condition_pro.await();
this.name = name+"---"+count++ ;
String threadName=Thread.currentThread().getName();
System.out.println(threadName+"生产"+this.name);
flag = true;
condition_con.signal();
}
finally{
lock.unlock();
}
}
public void out()throws InterruptedException{
lock.lock();
try{
while(!flag)
condition_con.await();
String threadName=Thread.currentThread().getName();
System.out.println(threadName+"消费----"+this.name);
flag = false;
condition_pro.signal();
}
finally{
lock.unlock();
}
}
}
class Producer implements Runnable
{
private Rescure res;
Producer(Rescure res){
this.res = res;
}
public void run(){
while(true){
try{
res.set("商品");
}catch(Exception e){}
}
}
}
class Consumer implements Runnable
{
private Rescure res;
Consumer(Rescure res){
this.res = res;
}
public void run(){
while(true){
try{
res.out();
}catch(Exception e){}
}
}
}