目录
同步异步(Synchronize, Asynchronize):
static boolean interrupted() 方法解释:
Synchronized实例方法和Synchronized阻挡
Synchronized类方法和Synchronized阻挡
进程:
进程可以视为程序的一个实例。大部分程序可以同时运行多个实例进程(例如记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如网易云音乐、360 安全卫士等)
进程是具有一定独立功能的程序、它是系统进行资源(内存)分配和调度的最小单位,重点在系统调度和单独的单位,也就是说进程是可以独立运行的一段程序。(进程是动态的,程序是静态的)
线程:
线程是进程的一个实体,是CPU调度和分派的基本单位,他是比进程更小的能独立运行的基本单位,线程自己基本上不拥有系统资源。(拥有自己的栈)
进程和线程的关系:
一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程(通常说的主线程)。
2、系统将资源分配给进程,同一进程的所有线程共享该进程的所有资源。
3、线程在执行过程中,需要协作同步(生产者消费者)。不同进程的线程间要利用消息通信的办法实现同步。
4、CPU是分给线程,即真正在CPU上运行的是线程。
线程通信方法:
详情Java 线程间通信_WYSCODER的博客-CSDN博客_java线程通信
在Java的Object类中提供了wait、notify、notifyAll等方法,这些方法可以实现线程间的通信,因为Object类是所有类的基类,因此所有的对象都具有线程间通信的方法。
void wait():调用一个对象的wait方法,会导致当前持有该对象的锁的线程等待,直到该对象的另一个持有锁的线程调用notify或者notifyAll唤醒。
void wait(long timeout):除了和wait相似,还具有超过定时的超时时间,时间到后或自动唤醒。
void wait(long timeout,int nanou):与 void wait(long timeout) 相同,不过提供了纳秒级别的更精确的超时控制。
void notify():调用一个对象的notify方法,会导致当前持有该锁的所有线程中的随机某一个线程被唤醒。
void notifyAll():调用一个对象的notifyAll方法,会导致当前持有该锁的所有线程被唤醒。
并行与并发:
单核 cpu 下,线程实际还是串行执行的。操作系统中有一个组件叫做任务调度器,将 cpu 的时间片(windows下时间片最小约为 15 毫秒,人能感知到的是0.1秒)分给不同的线程使用,只是由于 cpu 在线程间(时间片很短)的切换非常快,人类感觉是同时运行的 。总结为一句话就是: 微观串行,宏观并行 。
一般会将这种线程轮流使用 CPU 的做法称为并发, concurrent。
多核 cpu下,每个核(core) 都可以调度运行线程,这时候线程可以是并行的。
同步异步(Synchronize, Asynchronize):
需要等待结果返回,才能继续运行就是同步
不需要等待结果返回,就能继续运行就是异步
实现线程:
1、继承Thread类
2、实现Runnable接口
3、实现Callable接口,重写call方法(有返回值)
4、线程池
1和2最大的区别是:
因为Java里面是单继承的,继承Thread类方式将单继承这个位置给占了,只能去实现接口,不能再去继承别的类了。实现Runnable接口这种方式不影响继承类也不影响实现其他接口。
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread());
}
}
public class MyRunnable implements Runnable{
//Java是单继承的,使用Runnable接口不影响单继承
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
MyThread myThread = new MyThread();
MyRunnable runnable = new MyRunnable();
Thread myThread1 = new Thread(runnable);
//使用runnable仍然需要使用Thread去new
多线程的编程步骤:
1、第一步:创建资源类,在资源类创建属性和操作方法
2、第二步:在资源类中操作方法
1、判断(设置条件,比如容器有限)
2、业务代码(干活)
3、通知(notify或者notifyAll)
3、第三步:创建多个线程,调用资源类的操作方法。 (比如:生产者线程生产了蛋糕放到盘子panzi.putCake(),和消费者线程吃蛋糕panzi.getCake())
线程的创建中Thread(String name)name是给创建的线程签名。
只有start()才可以启动线程,线程中的run()方法只是普通的方法。
如果在一个线程中没有start(),直接run()就只是简单的顺序执行调用run()方法而已。
让线程暂时停止可以选择sleep()方法(nanos是精确到纳秒)。比如Thread.sleep(1000),当前线程睡眠1秒。需要知道的是,1秒后,线程是回到可执行状态,并不是执行状态,什么时候执行那是由虚拟机来决定的。所以sleep(1000)并不是在睡眠1秒后立即执行。sleep()方法并不会释放对象锁。
sleep()和wait()的区别?
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,将执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。
wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
yield:
解释它之前,先简述下,多线程的执行流程:多个线程并发请求执行时,由cpu决定优先执行哪一个,即使通过thread.setPriority(),设置了线程的优先级,也不一定就是每次都先执行它
Thread.yield();表示暂停当前线程,执行其他线程(包括执行yield这个线程), 执行谁由cpu决定
yield这个方法是让当前线程回到可执行状态,以便让具有相同优先级的线程进入执行状态(包括这个执行yield的线程,因为其也在可执行状态)
public static native void yield();
- Yield是一个静态的原生(native)方法
- Yield告诉当前正在执行的线程把运行机会交给线程池中拥有相同优先级的线程。
- Yield不能保证使得当前正在运行的线程迅速转换到可运行的状态
- 它仅能使一个线程从运行状态转到可运行状态,而不是等待或阻塞状态。
join:
在某些情况下,如果子线程里要进行大量的耗时的运算,主线程可能会在子线程执行完之前结束,但是如果主线程又需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()。
a.join(),在API中的解释是,在B线程中调用a.join(),堵塞当前线程B,直到A执行完毕并死掉,再执行B。//Waits for this thread to die.
在join()方法内设定超时,使得join()方法的影响在特定超时后无效。当超时时,主方法和任务线程申请运行的时候是平等的。然而,当涉及sleep时,join()方法依靠操作系统计时,所以你不应该假定join()方法将会等待你指定的时间。
public final void join() throws InterruptedException
像sleep,join通过抛出InterruptedException对中断做出回应。
join()方法使用示例
public class JoinExample {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
public void run() {
System.out.println("First task started");
System.out.println("Sleeping for 2 seconds");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("First task completed");
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
System.out.println("Second task completed");
}
});
t1.start();
t1.join(); Main线程等待t1线程执行完毕,在执行下面的操作
t2.start();
}
}
Output:
First task started
Sleeping for 2 seconds
First task completed
Second task completed
如何正确的停止线程:
java中有三种停止线程方法
1、使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。
2、使用stop方法方法强行终止线程,但是不推荐使用这个方法,因为stop不安全而且是已经被废弃的方法(加上了注解@Deprecated,废弃方法指的是不推荐使用的方法但是依然可以调用),还有suspend和resume都是废弃的方法。
3、使用interrupt方法中断线程。
interrupt()方法仅仅使线程中打了一个停止的标记,并不是真的停止线程。
this.interrupted() 测试当前线程是否已经中断。
this.isInterrupted() 测试线程是否已经中断。
中断线程
线程的thread.interrupt()方法是中断线程,将会设置该线程的中断状态位,即设置为true,之后的结果:线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身,并不是一定中断这个线程。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true)。
线程的暂时停止:
参考:
什么时候会抛出InterruptedException,如何处理InterruptedException_liao0801_123的博客-CSDN博客_interruptedexception
InterruptedException_王胖胖儿的博客-CSDN博客_interruptedexception
try{
Thread.sleep(1000);
}catch(InterruptedException e){
}
sleep方法的调用被放在try-catch里面,这是因为sleep方法可能会抛出一个称为InterruptedException的异常。catch是用来取消线程处理时的异常。
同样可能抛出异常的还有 wait(),join()
“半路唤醒”被Thread sleep暂停的线程,则可调用该线程的interrupt方法抛出异常,通过catch到异常以后在catch代码块中完成想要完成的操作。
public void interrupt() 方法解释:
代替废弃的stop()方法[暴力停止线程],不直接中止线程,而是传递给目标线程一个“应该关闭”的信号,由目标线程自行处理。
像Thread.suspend, Thread.stop这些方式,是因为由于没有使用可中断机制而被Deprecated。
“应该关闭”的信号的具体体现:
当目标线程处于阻塞状态,会抛出InterruptedException,而不会将标示位设置为true。
当目标线程处于非阻塞状态,会将标示位设置为true,不会抛出异常。
举例:当用户发起一项请求,后端的代码正在进行(可能此任务的代码运行时间比较长),这个时候用户想取消这个请求,如果直接调用stop()方法,则此线程会“戛然而止”,我们都知道,web的请求绝大情况下都会有对于数据库的操作,然后此时线程退出,则事务没有提交,这就会造成数据不一致的情况;或者该线程正在持有redis中的锁,那么这样的话,就会造成锁不能及时的释放,所以要使用interrupted()方法,但是这还不够,我们需要在代码中适当的位置检查中断请求,使用Thread.interrupted()静态方法,如果返回true,则进行相应的取消处理。
static boolean interrupted() 方法解释:
作用:测试当前线程是否已被中断:返回中断标示位,并且重置中断标示位为false。
Synchronized实例方法和Synchronized阻挡
Synchronized类方法和Synchronized阻挡
public class Something {
private Object lock1 = new Object();
private Object lock2 = new Object();
private Object lock3 = new Object();
//这里synchronized关键字拿到的锁是对象object的锁
//所有需要这个对象的锁的方法都不能同时执行。
//这是最常用的高并发场景下要锁住某个方法所用的操作。锁住同一个变量的方法块共享同一把锁。
public void lockMethod1() {
synchronized (lock1) {
}
}
public void lockMethod2() {
synchronized (lock1) {
}
}
public void lockMethod3() {
synchronized (lock2) {
}
}
// this
public synchronized void method1() {
}
public void method2() {
synchronized (this) {
}
// ...
}
// Something.class
public static synchronized void staticMethod1() {
}
public static synchronized void staticMethod2() {
synchronized (Something.class) {
}
}
}
Wait Set
Notify方法——从wait set拿出线程
使用notify()(通知)方法时,可以从wait set拿出一个线程。
obj.notify();(这个obj也是对象,不是线程)则从wait set里的线程中挑出一个,唤醒这个线程。被唤醒的线程便退wait set。
Notify后的线程
被notify唤醒的线程不是在notify后立即执行,因为在notify的那一刻,执行notify 的线程还握着锁定不放,所以其他线程无法获取该实例的锁定。
Notify如何选择线程
假设执行notify方法时,wait set里面正在执行的线程不止一个。规格并没有注明此时该选择哪一个线程。究竟是选择等待线程里面的第一个,随机选择或是另以其他方式选择,则以java处理系统而异。