将最近学习的JAVA多线程知识在这里进行总结和分享,以便以后查看。所有内容仅供参看,有不正确的地方欢迎指出。
一.基本概念
1.常见术语
- 多线程:指的是这个程序(一个进程)运行时产生了不止一个线程
- 并行:多个cpu实例或者多台机器同时执行一段处理逻辑
- 并发:通过cpu调度算法,不断切换时间片,执行线程.当一个线程处在运行状态时,其它线程处于挂起状态
- 线程安全:在并发情况下,一段逻辑经过多线程使用,线程的调度顺序不影响最终结果。
- 同步:使线程共享的资源始终保持一致和准确。
2,应该场景(多个线程调度可以优化系统性能)
- 网络请求分发的场景
- 文件导入
- 短信发送场景
二、Java中线程六种状态
- wait():持有对象锁的线程A准备释放对象锁权限,释放cpu资源并进入等待状态
- notify(): 线程A的synchronized 代码执行结束并且释放锁之后,线程X直接获得对象锁权限,其他竞争线程继续等待(线程x同步完毕释放对象锁,其他线程仍然等待,直至有新的notify,notifyAll被调用)
- notifyAll(): 会唤醒所有竞争同一对象锁的所有线程,线程A的synchronized 代码执行结束并且释放锁之后,所有被唤醒的线程都有可能获得对象锁权限。
注:wait(); notify():; notifyAll(): 三个方法都必须在synchronized 同步关键字所限定的作用域中调用
- yield():让出时间片,重新调度。当前线程调用此方法,告诉cpu吧运行机会交给线程池中拥有相同优先级的线程。不保证一定能达到让位的目的,让步的线程可能再次被调度程度选中。yield()不会导致阻塞。和sleep(o)效果上一样
- join():本质是wait/ notify,让线程的执行结果可见。 当前线程里调用其它线程的join方法,当前线程阻塞 ,直到线程执行完毕或者到达设定的时间
public class JoinDemo {
private static int i=10;
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(()->{
i=30;
try {Thread.sleep(3000); } catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
/两个线程仍是并行,可以继续其他操作
t.join(); // //t线程中的执行结果对于main线程可见.
//延迟3秒后输出i:30
System.out.println("i:"+i);
}
}
2. 线程状态查看
1)在控制台 执行 jps 命令获取线程Id
2)“ jstack 线程Id" 查看线程状态
三、线程的死锁
一组互相竞争资源的线程因互相等待,导致“永久”阻塞的现象
1.死锁发生的条件
1)互斥,共享资源 X 和 Y 只能被一个线程占用;
2)占有且等待,线程 T1 已经取得共享资源 X,在等待共享资源 Y 的时候,不释放共享资源 X;
3)不可抢占,其他线程不能强行抢占线程 T1 占有的资源;
4)循环等待,线程 T1 等待线程 T2 占有的资源,线程 T2 等待线程 T1 占有的资源,就是循环等待。
2.如何解决死锁问题
按照前面说的四个死锁的发生条件,互斥这个条件我们没有办法破坏,因为我们用锁为的就是互斥,其他三个条件,我们只需要破坏其中一个,就可以避免死锁的产生。
1)对于“占用且等待”这个条件,我们可以一次性申请所有的资源,这样就不存在等待了。
2)对于“不可抢占”这个条件,占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动 释放它占有的资源,这样不可抢占这个条件就破坏掉了。
3)对于“循环等待”这个条件,可以靠按序申请资源来预防。所谓按序申请,是指资源是有线性顺序的,申请的时候可以先申请资源序号小的,再申请资源序号大的,这样线性化后自然就不存在循环 了。
四、线程的创建
1.实现Runable接口实现run()方法
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
2.继承Thread类重写run方法。本质上是实现了 Runnable 接口的一个实例,通过 Thread类的 start() 实例方法。
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.start();
myThread2.start();
3.实现Callable 接口实现call()抽象方法,通过 FutureTask 包装器创建带返回值的线程(底层是实现了runnable接口和future接口)
public class Test implements Callable<String> {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Test callableDemo=new Test();
FutureTask<Integer> result = new FutureTask(callableDemo);
new Thread(result).start();
System.out.println(result.get());
}
@Override
public String call() throws Exception {
int a=1;
int b=2;
System.out.println(a+b);
return "执行结果:"+(a+b);
}
}
4.线程池:提供了一个线程队列,队列中保存着所有等待状态的线程。避免了创建与销毁额外开销,提高了响应的速度。
三.线程终止
1.stop()//强制终止,不建议使用,会破坏未执行完的线程
2.interrupt()//设置一个中断标志,通知线程自己处理,并终止。(处理权在线程,不在外部调用,处于阻塞的线程,不一定会终止)
1) 作用:
a.设置一个共享变量的值 true;
b.唤醒处于阻塞状态下的线程。
案例1:线程为非阻塞状态
public class InterruptDemo implements Runnable{
private int i=1;
@Override
public void run() {
// Thread.currentThread().isInterrupted()=false;(表示一个中断的标记 interrupted=fasle)
while(!Thread.currentThread().isInterrupted()){
System.out.println("Test:"+i++);
}
}
public static void main(String[] args) {
Thread thread=new Thread(new InterruptDemo());
thread.start();
thread.interrupt(); //设置 interrupted=true;
}
}
案例2:线程为阻塞状态
public class InterruptDemo02 implements Runnable{
@Override
public void run() {
while(!Thread.currentThread().isInterrupted()){ //false
try {
TimeUnit.SECONDS.sleep(200);
} catch (InterruptedException e) {
//InterruptedException异常会对isInterrupted进行复位为false (//Thread.interrupted() 方法也可以进行复位)
e.printStackTrace();
//抛出异常后线程不会结束
// 线程自己觉得进行操作。比如处理,比如再次中断
Thread.currentThread().interrupt(); //再次中断
//抛出异常。。
}
}
System.out.println("processor End");
}
public static void main(String[] args) throws InterruptedException {
Thread t1=new Thread(new InterruptDemo02());
t1.start();
Thread.sleep(1000);
t1.interrupt(); //有作用 true
}
}
流程:t1 运行 isInterrupted =false --->主线程睡1s ----->t1线程进入while循环,睡200s -----> 主线程时间结束执行t1.interrupt()---> isInterrupted =true----->t1线程被唤醒-----> 抛出InterruptedException 中断异常, isInterrupted被复位为false---->执行Thread.currentThread().interrupt()再次中断 -----> isInterrupted =true--->输出processor End
2)JVM调用的操作系统源码