JUC以及并发,线程同步,线程安全的概念加深
Posted XeonYu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JUC以及并发,线程同步,线程安全的概念加深相关的知识,希望对你有一定的参考价值。
JUC
JUC:就是JDK给我们提供好的一个工具包,提供了很多用于处理并发的类和接口。包名是 java.util.concurrent .取前三个字母就是JUC
我们先大致看一眼这个包里都有啥
可以看到,JUC包中有很多类和接口,我们之前用的Callable,FutureTask以及用来简化线程休眠的TimeUnit都在这个包中。
所以说,基本上,不管你是android开发还是Java开发,只要涉及到多线程并发的知识,这个包的代码你是一定会用到的,所以java.util.concurrent这个包里的类和接口非常重要
之前在本专栏的第一篇文章中已经提到过多线程和并发的概念。本篇我们再来加深下对并发,线程安全,和线程同步概念的理解。
并发(concurrent)
首先并发的种类分为两种
- 交替执行 : 在单核的cpu中,多个线程轮流占用cpu资源交替去执行任务(操作资源)
- 并行执行 : 在多核的cpu中,多个线程并行的去执行任务(操作资源)
以上两种情况,交替执行和并行执行都是并发.
线程安全
上面我们说到了多线程并行执行,那假如多个线程同时去操作一个资源,两个线程同时写一个值,另外一个线程又同时在读这个值,那这个值最终读出来的是哪个线程写进去的值呢?
因为多线程的执行时机是由cpu调度的,所以最终读出来的值到底是哪个我们是没办法保证的,这个就是线程安全问题。
之前的多线程卖票和线程不安全的单例模式都属于线程安全问题,我们再来看看另外一个例子。
以下代码中,我们在for循环中给一个ArrayList中添加元素。
public static void main(String[] args) {
ArrayList<Integer> integers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
/*在单个线程向数组中添加元素*/
integers.add(i);
System.out.println(Thread.currentThread().getName() + ":integers = " + integers);
}
}
这个运行是肯定没问题的,因为我们是在一个线程中操作的ArrayList
运行结果如下:
下面我们来改一改代码,在多线程中往ArrayList中添加元素试一下:
public static void main(String[] args) {
ArrayList<Integer> integers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
/*启动多个线程向数组中添加元素*/
int finalI = i;
new Thread(() -> {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
integers.add(finalI);
System.out.println(Thread.currentThread().getName() + ":integers = " + integers);
}).start();
}
}
然后我们多运行几次试试
运行结果可以看到,有很大几率出现打印的值有不正确的值,且会出现ConcurrentModificationException这个异常。这个异常翻译起来很好理解,叫做: 并发修改异常
原因是ArrayList的add方法是线程不安全的方法,所以在多线程中通过add方法向集合中添加数据时,就可能会出现该异常。
解决这个异常也很简单,在修改数据的地方做线程同步操作
线程同步
当多个线程要对同一个内存地址进行操作时(一般都是写操作),同一时间只能有一个线程对该内存地址进行操作,其他线程不可以对该内存地址进行操作,此时,其他线程处于BLOCKED (阻塞)状态,这个就是线程同步。
线程同步是为了解决线程安全问题。
在之前文章的多线程卖票例子中已经处理过线程同步了,使用的是JVM提供的synchronized关键字。
其实,jdk也给我提供了一些线程安全的集合来给我们使用。
比如使用java.util包中的Vector。
我们先来看看ArrayList的add方法和Vector的add方法有什么区别:
首先是ArrayList的add
再来看看Vector的add
可以看到,Vector的add方法使用了synchronized修饰,是同步方法。
下面我们将ArrayList替换为Vector试一下。
public static void main(String[] args) {
Vector<Integer> integerVector = new Vector<>();
for (int i = 0; i < 10; i++) {
/*启动多个线程向数组中添加元素*/
int finalI = i;
new Thread(() -> {
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*Vector的add是线程安全的*/
integerVector.add(finalI);
System.out.println(Thread.currentThread().getName() + ":integerVector = " + integerVector);
}).start();
}
}
运行看看效果:
可以看到,数据是正常的,且不会报ConcurrentModificationException这个异常。
本篇文章主要是初步对JUC的认识,以及加深一下对多线程并发以及线程同步,线程安全这些概念的理解。
除了使用线程安全的集合和synchronized关键字以外,我们还可以使用JUC提供的锁来实现线程同步,下一篇我们来看一下JUC中的锁。
下一篇:
java.util.concurrent.locks 包中的接口和实现类
如果你觉得本文对你有帮助,麻烦动动手指顶一下,可以帮助到更多的开发者,如果文中有什么错误的地方,还望指正,转载请注明转自喻志强的博客 ,谢谢!
以上是关于JUC以及并发,线程同步,线程安全的概念加深的主要内容,如果未能解决你的问题,请参考以下文章
java.util.concurrent.locks 包中的接口和实现类
java.util.concurrent.locks 包中的接口和实现类