这个阶段是最后一个阶段,集合框架和反射最后再去进行总结。接下来就开始Spring的学习。
死锁
死锁平常是比较难复现的,符合以下四个条件,就会发生死锁,破坏死锁就是打破以下四个条件:
1、互斥使用,即当该资源被一个线程使用时,其它线程不能再去使用该资源 2、不可抢占,资源请求者不能强制从资源占有者手中去抢夺资源 3、请求和保持,即当资源对其它资源进行请求获取时候,原有资源不去进行释放 4、循环等待 - 类似于循环依赖
死锁的代码的实现
public class ThreadSyncDem_one {
public static void main(String[] args) {
Object o = new Object();
Object p = new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (o){
System.out.println("线程1对o资源进行占用");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (p){
System.out.println("线程1对资源p进行占用");
}
}
}
}
).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (p){
System.out.println("线程2对资源p进行占用");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o){
System.out.println("线程2对资源o进行占用");
}
}
}
}).start();
}
}
原子性
原子性,顾名思义,要么同时成功,要么同时失败,类似于事务这种。volatitle只能保证可见性,不能保证原子性,因此引入AtomicInteger(Atomic还有其它实现,比如布尔类型或者其它类型)和锁的机制。
1、首先看锁(synchronized)是如何保证原子性的,实现方法就是加锁,但锁保证的不是原子性而是顺序性
public class VolatileDem_one {
public static void main(String[] args) {
MyRun myRun = new MyRun();
for (int i = 1; i <= 100; i++) {
new Thread(myRun).start();
}
}
}
class MyRun implements Runnable {
// private volatile int count;
private int count;
@Override
public void run() {
//锁对象保持唯一即可
synchronized (MyRun.class) {
for (int i = 1; i <= 100; i++) {
count++;
System.out.println(count);
}
}
}
}
2、就是CAS机制去进行实现——比较之后再去进行实现,从主内存中去进行取值和插值,Atomic就是这种机制
(1)先去主内存中去进行取值 (2)取值之后去进行操作 (3)操作之后值在主内存中去进行更新,当达到预设,往下走,当未达到预设,重新往下走
实现如下
public class VolatileDem_two {
public static void main(String[] args) {
MyRun myRun1 = new MyRun();
for (int i = 1; i <= 100; i++) {
new Thread(myRun1).start();
}
}
}
class MyRun1 implements Runnable {
//直接是一个Integer的源自类 - 提供了多种方法 — 还有个Boolean类
private AtomicInteger atomicInteger = new AtomicInteger();
// private volatile int count;
@Override
public void run() {
//锁对象保持唯一即可
// synchronized (MyRun.class) {
for (int i = 1; i <= 100; i++) {
atomicInteger.getAndAdd(1);
System.out.println(atomicInteger.incrementAndGet());
// }
}
}
}
其中Atomic常用的API如下
int get() - 原子性的进行值的获取 int gatAndIncream() - 获取自增前的值再加上一 int incrementAndGet() - 加一再获取原子值 int addGet(int value) - 以原子值方式加对应的值再获取 int getAndSet(int value)
原子性也是目前必须考虑的一个特性之一
并发包下提供的可用类
1、ConcurrentHashMap
首先HashMap虽然插入很快,但线程不安全,HashTable虽然线程安全,但过于笨重——所有的操作都进行了悲观锁的添加,当操作过多时都要进行等待,因此推出ConcurrentHashMap去进行操作 只锁定桶,相当于只锁定当前索引操作,其它索引操作不进行锁定。ConcurrentHashMap类似于CAS这种实现,比较再判别。
public class ConcurrentHashMap {
// public static Map<String, String> stringMap = new HashMap<>();
// public static Map<String, String> stringMap = new Hashtable<>();
public static Map<String, String> stringMap = new java.util.concurrent.ConcurrentHashMap<>();
public static void main(String[] args) {
MyRunAble runAble = new MyRunAble();
Thread t1 = new Thread(runAble, "test");
Thread t2 = new Thread(runAble, "admin");
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
//线程不安全的
System.out.println(stringMap.size());
}
}
class MyRunAble implements Runnable{
@Override
public void run() {
for (int i = 0; i < 500000; i++){
ConcurrentHashMap.stringMap.put(Thread.currentThread().getName()+" " + i, String.valueOf(i));
}
}
}
2、CountDownLatch
操控这个方法的API就只有两个,一个await就是进行等待,countdown就是对操作数(操作数可以去进行设定)去进行减一,当操作数为0时候,去进行count控制的线程的唤醒,类似于一个控制器。具体实现如下
public class CountDownLatchDem {
public static void main(String[] args) throws InterruptedException {
CountDownLatch c = new CountDownLatch(1);
ThreadA threadA = new ThreadA(c);
ThreadB threadB = new ThreadB(c);
threadA.start();
threadB.start();
}
}
class ThreadA extends Thread{
private CountDownLatch count;
public ThreadA(CountDownLatch c) {
this.count = c;
}
@Override
public void run() {
System.out.println("A");
try {
//当执行完本线程之后去进行等待
count.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("C");
}
}
class ThreadB extends Thread{
private CountDownLatch countDownLatch;
public ThreadB(CountDownLatch c) {
this.countDownLatch = c;
}
@Override
public void run() {
System.out.println("B");
//操作计数器,计数器减去一,去进行其它线程的获取
countDownLatch.countDown();
}
}
止于此,接下来是Spring...