多线程存在的问题:
竞争条件带来的错误:
public class Main {
public static Map<Integer, Integer> map = new HashMap<>();
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new java.lang.Thread(Main::putIfAbsent).start();
}
}
public static void putIfAbsent() {
int r = new Random().nextInt(10);
if (!map.containsKey(r)) {
map.put(r, r);
System.out.println("Put" + r);
}
}
}
运行结果:
可以看到9出现了两次
死锁
public class Main {
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
public static void main(String[] args) {
new Thread1().start();
new Thread2().start();
}
static class Thread1 extends java.lang.Thread {
@Override
public void run() {
synchronized (lock1) {
try {
java.lang.Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("");
}
}
}
}
static class Thread2 extends java.lang.Thread {
@Override
public void run() {
synchronized (lock2) {
try {
java.lang.Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("");
}
}
}
}
运行一下
排查死锁:
寻找死锁的id
在Terminal里输入
jps
获得进程ID
接着输入
jstack "进程Id"
然后自己看问题在哪
预防死锁产生的原则
所有线程都按照相同顺序获得锁
实现线程安全的手段
synchronized
静态方法 这种情况下把Main class当成锁了
public class Access {
private static Integer i=0;
public static void main(String[] args) {
for (int i = 0; i <1000 ; i++) {
new java.lang.Thread(Access::slowFileOperation).start();
}
}
public synchronized static void slowFileOperation() {
try {
java.lang.Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
System.out.println("i"+i);
}
也可以这样写
public class Access {
public static final Object lock =new Object();
private static Integer i=0;
public static void main(String[] args) {
for (int i = 0; i <1000 ; i++) {
new java.lang.Thread(Access::slowFileOperation).start();
}
}
public synchronized static void slowFileOperation() {
try {
java.lang.Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock){
i++;
System.out.println("i"+i);
}
对于非静态方法,实际上是将new出来的对象当做了锁
public class Access {
public static final Object lock =new Object();
private static Integer i=0;
public static void main(String[] args) {
Access access=new Access();
for (int i = 0; i <1000 ; i++) {
new java.lang.Thread(access::slowFileOperation).start();
}
}
synchronized void slowFileOperation() {
try {
java.lang.Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock){
i++;
System.out.println("i"+i);
}
所有的实现Collection接口的类都是不安全的,包括所有的List,Map,例如:
public class Access {
public static Map<Integer, Integer> map = new HashMap<>();
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
new java.lang.Thread(Access::put).start();
}
}
public static void put() {
try {
java.lang.Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
Integer i = new Random().nextInt();
map.put(i, i);
for (Integer j : map.keySet()) {
out.println(i);
}
运行结果
解决方法:
可以在方法前加个synchronized,也可以使用JUC包 (java util concurrent):
public static Map<Integer, Integer> map = new ConcurrentHashMap<>();
Atomic Integer
public class Access {
public static int i = 0;
public static Set<Integer> set = new HashSet<>();
public static void main(String[] args) {
for (int j = 0; j < 100; j++) {
new java.lang.Thread(Access::put).start();
}
}
public static void put() {
try {
java.lang.Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (i < 1000) {
set.add(i);
out.println(i);
i = i + 1;
}
输出结果:
使用AtomicInteger后:
以后再解决