乐趣区

全栈之路JAVA基础课程三20190614v10

欢迎进入 JAVA 基础课程

本系列文章将主要针对 JAVA 一些基础知识点进行讲解,为平时归纳所总结,不管是刚接触 JAVA 开发菜鸟还是业界资深人士,都希望对广大同行带来一些帮助。若有问题请及时留言或加 QQ:243042162。

谨记:
最近习大大大力倡导 “不忘初心、牢记使命” 的主题教育,提出了 “守初心、担使命、找差距、抓落实” 的总体要求,这正好也映射在了我们个人生活和工作中。人到中年,最多的是懒,缺乏了学生时代的初心,不管你处于哪个年纪,请摆脱借口,端正态度,以家庭为寄托,以未来为展望,将技术提升和家庭教育落到实处,让自己有生之年还能得到质的飞跃。

并发和多线程

1. 进程和线程

进程:具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。

线程:进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

区别:进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫做轻量级进程。

a. 创建线程的几种方式
(1)继承 Thread
(2)实现 Runnable 接口
(3)应用程序可以使用 Executor 框架来创建线程池
b. 线程的几种状态(早上打车去上班)
(1)新建(准备叫一辆嘀嘀打车)
(2)可运行(找到一辆可以带你去上班的车)
(3)运行(司机接到你,带你去上班)
(4)阻塞(路上堵车了): 等待阻塞 -wait、同步阻塞 - 同步锁、其他阻塞 -
Thread.sleep(long ms) 或 t.join ()方法
(5)死亡(到公司了,付钱下车)
c. 同步方法和同步代码块
同步方法默认用 this 或者当前类 class 对象作为锁;
同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法。

死锁

  • 概念:两个线程或两个以上线程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是这些线程都陷入了无限的等待中。
  • 举例:某计算机系统中只有一台打印机和一台输入 设备,进程 P1 正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程 P2 所占用,而 P2 在未释放打印机之前,又提出请求使用正被 P1 占用着的输入设备。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。
  • 产生死锁原因:
    1. 系统资源的竞争
    2. 进程推进顺序非法
    3. 死锁产生的必要条件

    产生死锁必须同时满足以下四个条件,只要其中任一条件不成立,死锁就不会发生。
    (1)互斥条件:进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。
    (2)不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。
    (3)请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
    (4)循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。即存在一个处于等待状态的进程集合 {Pl, P2, …, pn},其中 Pi 等 待的资源被 P(i+1) 占有(i=0, 1, …, n-1),Pn 等待的资源被 P0 占有。

  • 代码块

/**
 * 死锁实例
 * t1 先运行,这个时候 flag==true, 先锁定 obj1, 然后睡眠 1 秒钟
 * 而 t1 在睡眠的时候,另一个线程 t2 启动,flag==false, 先锁定 obj2, 然后也睡眠 1 秒钟
 * t1 睡眠结束后需要锁定 obj2 才能继续执行,而此时 obj2 已被 t2 锁定
 * t2 睡眠结束后需要锁定 obj1 才能继续执行,而此时 obj1 已被 t1 锁定
 * t1、t2 相互等待,都需要得到对方锁定的资源才能继续执行,从而死锁。*/
 class DeadLock implements Runnable{private static Object obj1 = new Object();
    private static Object obj2 = new Object();
    private boolean flag;

    public DeadLock(boolean flag){this.flag = flag;}

    @Override
    public void run(){System.out.println(Thread.currentThread().getName() + "运行");

        if(flag){synchronized(obj1){System.out.println(Thread.currentThread().getName() + "已经锁住 obj1");
                try {Thread.sleep(1000);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                synchronized(obj2){
                    // 执行不到这里
                    System.out.println("1 秒钟后,"+Thread.currentThread().getName()
                            + "锁住 obj2");
                }
            }
        }else{synchronized(obj2){System.out.println(Thread.currentThread().getName() + "已经锁住 obj2");
                try {Thread.sleep(1000);
                } catch (InterruptedException e) {e.printStackTrace();
                }
                synchronized(obj1){
                    // 执行不到这里
                    System.out.println("1 秒钟后,"+Thread.currentThread().getName()
                            + "锁住 obj1");
                }
            }
        }
    }

}
public class LockDemo {public static void main(String[] args) {Thread t1 = new Thread(new DeadLock(true), "线程 1");
        Thread t2 = new Thread(new DeadLock(false), "线程 2");

        t1.start();
        t2.start();}
}

运行结果

线程 2 运行
线程 1 运行
线程 1 已经锁住 obj1
线程 2 已经锁住 obj2
  • 避免死锁

    • 加锁顺序(线程按照一定的顺序加锁)
    • 加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)
    • 死锁检测

Thread t2 = new Thread(new DeadLock(false), "线程 2");

改成

Thread t2 = new Thread(new DeadLock(true), "线程 2");
退出移动版