脚本宝典收集整理的这篇文章主要介绍了集合的线程安全问题,脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。
我们知道集合中的Vector是线程安全的,其他的比如ArrayList、LinkedList、HashSet等等都是线程不安全的。
ArrayList:
import java.util.ArrayList;
import java.util.Collection;
public class ConllectionJUCtest {
public static void main(String[] args){
Collection<Integer> collection=new ArrayList();
for(int i=0;i<100;i++){
new Thread(new Runnable() {
@override
public void run() {
collection.add(1);
}
}).start();
}
System.out.PRintln(collection.toString());
}
}
并发修改错误
import java.util.ArrayList;
import java.util.Collection;
import java.util.Vector;
public class ConllectionJUCTest {
public static void main(String[] args){
Collection<Integer> collection=new Vector<Integer>();
for(int i=0;i<100;i++){
new Thread(new Runnable() {
@Override
public void run() {
collection.add(1);
}
}).start();
}
System.out.println(collection.toString());
}
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class ConllectionJUCTest {
public static void main(String[] args){
Collection<Integer> collection= Collections.synchronizedList(new ArrayList<>());
for(int i=0;i<100;i++){
new Thread(new Runnable() {
@Override
public void run() {
collection.add(1);
}
}).start();
}
System.out.println(collection.toString());
}
}
很多时候,我们的系统应对的都是读多写少的并发场景。CopyOnWriteArrayList容器允许并发读,读操作是无锁的,性能较高。至于写操作,比如向容器中添加一个元素,则首先将当前容器复制一份,然后在新副本上执行写操作,结束之后再将原容器的引用指向新容器。
CopyOnWriteArrayList实现原理及源码分析 - dreamcatcher-cx - 博客园 (cnblogs.COM)
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CopyOnWriteArrayList;
public class ConllectionJUCTest {
public static void main(String[] args){
Collection<Integer> collection= new CopyOnWriteArrayList<>();
for(int i=0;i<100;i++){
new Thread(new Runnable() {
@Override
public void run() {
collection.add(1);
}
}).start();
}
System.out.println(collection.toString());
}
}
sleep进入堵塞状态是不会释放对象锁的,wait、join都会释放对象锁
public class GengTest {
public static void main(String[] args) throws InterruptedException {
Geng geng=new Geng();
Geng geng1=new Geng();
new Thread(new Runnable() {
@Override
public void run() {
try {
geng.getPhone();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
//1、第一种实验,此处睡眠一秒 结果 phone sms
//2、第二种实验,将两个方法改为Synchronized方法,然后此处睡眠一秒 结果 phone sms
//Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run() {
try {
//4、第四种实验,将两个方法改为Synchronized方法,定义两个Geng对象,然后此处睡眠一秒 结果 sms phone
geng1.getSMS();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
class Geng{
//5、第五种实验,将两个方法改为Synchronized 静态方法,定义两个Geng对象然后此处睡眠一秒 结果 phone sms
public static synchronized void getSMS() throws InterruptedException {
System.out.println("sms");
}
public static synchronized void getPhone() throws InterruptedException {
//3、第S三种实验,将两个方法改为Synchronized方法,然后此处睡眠一秒 结果 phone sms
Thread.sleep(1000);
System.out.println("phone");
}
}
公平锁:多个线程按照申请锁的顺序去获得锁,线程会直接进入队列去排队,永远都是队列的第一位才能得到锁。
非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列,如果能获取到,就直接获取到锁。
我们使用的synchronized(隐式) 、 lock(显式)都是可重入锁
证明:
import java.util.concurrent.locks.ReentrantLock;
public class GengTest {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
Object o=new Object();
synchronized (o){
System.out.println("第一层");
synchronized (o){
System.out.println("第二层");
synchronized (o){
System.out.println("第三层");
}
}
}
}
}).start();
}
}
这个例子中,我们可以看到,会顺序输出第一层、第二层、第三层,按理说,当我们执行第一层的时候,此时还没有释放o,然后就再次加锁,应该会出现异常,但是由于对象锁式可重入锁,所以不会
import java.util.concurrent.locks.ReentrantLock;
public class GengTest {
public static synchronized void geng(int i ){
System.out.println(i);
geng(i+1);
}
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
geng(0);
}
}).start();
}
}
同上,由于是可重入锁,所以会导致栈溢出。
不同的线程分别占用对方需要的资源不放弃,都在等待对方放弃自己需要的同步资源(共享资源),就形成了死锁。
出现了死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续
出现的死锁的可能原因:
1、系统资源不足
2、进程运行的顺序不合适
3、资源分配不当
import java.util.concurrent.locks.ReentrantLock;
public class GengTest {
public static void main(String[] args) {
Object a=new Object();
Object b=new Object();
new Thread(new Runnable() {
@Override
public void run() {
synchronized(a){
System.out.println("1正在使用a");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b){
System.out.println("1正在使用b");
}
}
}
}).start();
new Thread(new Runnable() {
public void run() {
synchronized(b){
System.out.println("2正在使用b");
synchronized (a){
System.out.println("2正在使用a");
}
}
}
}).start();
}
}
验证是否为死锁:
1、jps (类似于linux的 ps -ef) jstack(jvm自带的堆栈跟踪工具)
和Runnable相比,Callable更加强大。它可以有返回值;也可以抛出异常;支持泛型的返回值。
Future接口:可以对具体的Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等等
FutureTask是Future接口的唯一实现类,同时实现了Runnable、Future接口,说明他既可以作为Runnable被线程执行,也可以作为Future得到Callable的返回值。(FutureTask起到了沟通Runnable和Callable的作用)
FutureTask定义的内部变量
private static final int NEW = 0; //任务新建和执行中
private static final int COMPLETING = 1; //任务将要执行完毕
private static final int NORMAL = 2; //任务正常执行结束
private static final int EXCEPTIONAL = 3; //任务异常
private static final int CANCELLED = 4; //任务取消
private static final int INTERRUPTING = 5; //任务线程即将被中断
private static final int INTERRUPTED = 6; //任务线程已中断
总结下,FutureTask的状态流转过程,可以出现以下四种情况:
1、任务正常执行并返回。 NEW -> COMPLETING -> NORMAL
2、执行中出现异常。NEW -> COMPLETING -> EXCEPTIONAL
3、任务执行过程中被取消,并且不响应中断。NEW -> CANCELLED 4、任务执行过程中被取消,并且响应中断。 NEW -> INTERRUPTING -> INTERRUPTED
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.ReentrantLock;
public class GengTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<Integer> futureTask1=new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName());
return 200;
}
});
Thread thread1=new Thread(futureTask1,"线程1");
thread1.start();
//futureTask1.cancel(true);
//System.out.println(futureTask1.get());
FutureTask<Integer> futureTask2=new FutureTask<>(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName());
return 300;
}
});
Thread thread2=new Thread(futureTask2,"线程2");
thread2.start();
}
}
以上是脚本宝典为你收集整理的集合的线程安全问题全部内容,希望文章能够帮你解决集合的线程安全问题所遇到的问题。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。