标签:another waiting while 锁对象 tps cal main方法 catch 控制
目标:
1.基本概念
程序:是一个静态的概念;
进程:是一个动态的概念
a.进程是程序的一次动态执行过程,占用特定的地址空间;
b.每个进程都是独立的,包括三部分:CPU code data
c.缺点:内存的浪费,增加cpu的负担
线程:Thread,是进程中的一个‘单一的连续控制流程/执行路径‘;
a.线程又被称为轻量级进程;
b.Thread run at the same time,independently of one Another (线程同时运行,彼此独立)
c.一个进程可以拥有多个并行的线程
d.一个进程中的线程共享相同的内存单元/可以变换的内存空间地址-->可以访问相同的变量和对象,而且他们从统一堆中分配对象|通信|数据交换|同步操作
e.由于线程间的通信是在同一内存地址空间中进行的,不需要额外的通信机制,这就使线程间的通信更简便而且信息传递的速度也更快
2.线程的启动
2.1 实现Runnable接口
a.自定义一个线程 实现Runnable的run()方法 run()方法就是要执行的内容,会在另一个分支进行.Thread类本身也实现了Runnable接口
b.主方法中new一个自定义线程对象,然后new一个Thread类对象,其构造方法的参数就是自定义线程对象
c.执行Thread类的start方法,线程开始执行 自此产生了分支,一个分支会执行run方法,在主方法中不会等待run方法调用完毕返回才继续执行,而是直接继续执行,是第二个分支。这两个分支并行运行
PS:这里运用了静态代理模式: Thread类和自定义线程类都实现了Runnable接口 Thread类是代理Proxy,自定义线程类是被代理类 通过调用Thread的start方法,实际上调用了自定义线程类的start方法(当然除此之外还有其他的代码)
2.2 继承Thread类
a.自定义一个类MyThread,继承Thread类,重写run方法
b.在main方法中new一个自定义类,然后直接调用start方法 两个方法比较而言第二个方法代码量较少 但是第一个方法比较灵活,自定义线程类还可以继承其他的类,而不限于Thread类
2.3 实现Callable接口
3. 线程的状态
初始态:NEW
创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态。
在Java中,运行态包括就绪态 和 运行态。
该状态下的线程已经获得执行所需的所有资源,只要CPU分配执行权就能运行。 所有就绪态的线程存放在就绪队列中。
获得CPU执行权,正在执行的线程。 由于一个CPU同一时刻只能执行一条线程,因此每个CPU每个时刻只有一条运行态的线程。
阻塞态专指请求排它锁失败时进入的状态。
当前线程中调用wait、join、park函数时,当前线程就会进入等待态。 进入等待态的线程会释放CPU执行权,并释放资源(如:锁),它们要等待被其他线程显式地唤醒。
当运行中的线程调用sleep(time)、wait、join、parkNanos、parkUntil时,就会进入该状态; 进入该状态后释放CPU执行权 和 占有的资源。 与等待态的区别:无需等待被其他线程显式地唤醒,在一定时间之后它们会由系统自动唤醒。
线程执行结束后的状态。
4. 线程的方法
isAlive活着的定义是就绪、运行、阻塞状态 线程是有优先级的,优先级高的获得Cpu执行时间长,并不代表优先级低的就得不到执行
sleep时持有的锁不会自动释放,sleep时可能会抛出InterruptedException。 Thread.sleep(long millis) 一定是当前线程调用此方法,当前线程进入TIME_WAIT状态,但不释放对象锁,millis后线程自动苏醒进入READY状态。作用:给其它线程执行机会的最佳方式。
t.join()/t.join(long millis) 当前线程里调用线程1的join方法,当前线程进入WAIT状态,但不释放对象锁,直到线程1执行完毕或者millis时间到,当前线程进入可运行状态。 join方法的作用是将分出来的线程合并回去,等待分出来的线程执行完毕后继续执行原有线程。类似于方法调用。(相当于调用thead.run())
Thread.yield(),一定是当前线程调用此方法,当前线程放弃获取的cpu时间片,由运行状态变会可运行状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不保证一定会轮流执行。实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。Thread.yield()不会导致阻塞。
interrupt一个其他线程t时
如何停止线程?
但是注意! 如果线程中有阻塞操作,在阻塞时是无法去检测中断标志位或自定义标志位的,只能使用1)的interrupt方法才能中断线程,并且在线程停止前关闭引起阻塞的资源(比如Socket)。
obj.notify()唤醒在此对象监视器上等待的单个线程,选择是任意性的。notifyAll()唤醒在此对象监视器上等待的所有线程。
等待方(消费者)和通知方(生产者)
等待方:
synchronized(obj){
while(条件不满足){
obj.wait();
}
消费;
}
通知方:
synchonized(obj){
改变条件;
obj.notifyAll();
}
1)条件谓词:
将与条件队列相关联的条件谓词以及在这些条件谓词上等待的操作都写入文档。
在条件等待中存在一种重要的三元关系,包括加锁、wait方法和一个条件谓词。在条件谓词中包含多个状态变量,而状态变量由一个锁来保护,因此在测试条件谓词之前必须先持有这个锁。锁对象和条件队列对象(即调用wait和notify等方法所在的对象)必须是同一个对象。
当线程从wait方法中被唤醒时,它在重新请求锁时不具有任何特殊的优先性,而要去其他尝试进入同步代码块的线程一起正常地在锁上进行竞争。
每一次wait调用都会隐式地与特定的条件谓词关联起来。当调用某个特定条件谓词的wait时,调用者必须已经持有与条件队列相关的锁,并且这个锁必须保护着构成条件谓词的状态变量。
2)过早唤醒: 虽然在锁、条件谓词和条件队列之间的三元关系并不复杂,但wait方法的返回并不一定意味着线程正在等待的条件谓词已经变成真了。 当执行控制重新进入调用wait的代码时,它已经重新获取了与条件队列相关联的锁。现在条件谓词是不是已经变为真了?或许。在发出通知的线程调用notifyAll时,条件谓词可能已经变成真,但在重新获取锁时将再次变成假。在线程被唤醒到wait重新获取锁的这段时间里,可能有其他线程已经获取了这个锁,并修改了对象的状态。或者,条件谓词从调用wait起根本就没有变成真。你并不知道另一个线程为什么调用notify或notifyAll,也许是因为与同一条件队列相关的另一个条件谓词变成了真。一个条件队列与多个条件谓词相关是一种很常见的情况。 基于所有这些原因,每当线程从wait中唤醒时,都必须再次测试条件谓词。
3)notify与notifyAll: 由于多个线程可以基于不同的条件谓词在同一个条件队列上等待,因此如果使用notify而不是notifyAll,那么将是一种危险的操作,因为单一的通知很容易导致类似于信号地址(线程必须等待一个已经为真的条件,但在开始等待之前没有检查条件谓词)的问题。
只有同时满足以下两个条件时,才能用单一的notify而不是notifyAll:
判断当前线程是否正在执行 注意优先级是概率而非先后顺序(优先级高可能会执行时间长,但也不一定)
线程优先级特性:
在Runnable的run方法中不能抛出异常,如果某个异常没有被捕获,则会导致线程终止。
要求异常处理器实现Thread.UncaughtExceptionHandler接口。 可以使用setUncaughtExceptionHandler方法为任何一个线程安装一个处理器, 也可以使用Thread.setDefaultUncaughtExceptionHandler方法为所有线程安装一个默认的处理器;
如果不安装默认的处理器,那么默认的处理器为空。如果不为独立的线程安装处理器,此时的处理器就是该线程的ThreadGroup对象 ThreadGroup类实现了Thread.UncaughtExceptionHandler接口,它的uncaughtException方法做如下操作:
如果是由线程池ThreadPoolExecutor执行任务,只有通过execute提交的任务,才能将它抛出的异常交给UncaughtExceptionHandler,而通过submit提交的任务,无论是抛出的未检测异常还是已检查异常,都将被认为是任务返回状态的一部分。如果一个由submit提交的任务由于抛出了异常而结束,那么这个异常将被Future.get封装在ExecutionException中重新抛出。
标签:another waiting while 锁对象 tps cal main方法 catch 控制
原文地址:https://www.cnblogs.com/assistants/p/12010158.html