使用三个线程分别打印a、b、c三个字母,要求三个字母按照顺序打印指定次数,通过模拟线程不同的消耗时间来对比使用wait和不使用wait的性能的差异。
模拟每个线程只执行短暂时间
public class Test1 {
private static int status = 1;
private static int times = 100000;
private static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
while (times > 1) {
while (status != 1) {
}
System.out.print("A");
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
status = 2;
}
});
Thread thread2 = new Thread(() -> {
while (times > 1) {
while (status != 2) {
}
System.out.print("B");
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
status = 3;
}
});
Thread thread3 = new Thread(() -> {
while (times > 0) {
while (status != 3) {
}
System.out.println("C");
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
status = 1;
times--;
}
});
long start = System.currentTimeMillis();
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
public class Test2 {
private static int status = 1;
private static int times = 100000;
private static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
while (times > 1) {
while (status != 1) {
synchronized (obj) {
try {
obj.notifyAll();
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.print("A");
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
status = 2;
}
synchronized (obj) {
obj.notifyAll();
}
});
Thread thread2 = new Thread(() -> {
while (times > 1) {
while (status != 2) {
synchronized (obj) {
try {
obj.notifyAll();
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.print("B");
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
status = 3;
}
synchronized (obj) {
obj.notifyAll();
}
});
Thread thread3 = new Thread(() -> {
while (times > 0) {
while (status != 3) {
synchronized (obj) {
try {
obj.notifyAll();
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("C");
// try {
// Thread.sleep(10);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
status = 1;
times--;
}
});
long start = System.currentTimeMillis();
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
Test1与Test2的运行时间进行对比可以发现Test2使用wait需要更长的运行时间,而Test1循环等待需要耗费更少的时间,在CPU使用率图上左边为运行Test1时的CPU使用率,右边为Test2的CPU使用率,虽然不是百分百准确,但是总体上可以看出二者的运行时CPU使用率相差不大。
线程需要运行相对较长时间情况
通过在两个线程运行中间加入睡眠时间,模拟线程需要长时间运行的情况
public class Test1 {
private static int status = 1;
private static int times = 1000;
private static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
while (times > 1) {
while (status != 1) {
}
System.out.print("A");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
status = 2;
}
});
Thread thread2 = new Thread(() -> {
while (times > 1) {
while (status != 2) {
}
System.out.print("B");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
status = 3;
}
});
Thread thread3 = new Thread(() -> {
while (times > 0) {
while (status != 3) {
}
System.out.println("C");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
status = 1;
times--;
}
});
long start = System.currentTimeMillis();
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
public class Test2 {
private static int status = 1;
private static int times = 1000;
private static Object obj = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
while (times > 1) {
while (status != 1) {
synchronized (obj) {
try {
obj.notifyAll();
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.print("A");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
status = 2;
}
synchronized (obj) {
obj.notifyAll();
}
});
Thread thread2 = new Thread(() -> {
while (times > 1) {
while (status != 2) {
synchronized (obj) {
try {
obj.notifyAll();
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.print("B");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
status = 3;
}
synchronized (obj) {
obj.notifyAll();
}
});
Thread thread3 = new Thread(() -> {
while (times > 0) {
while (status != 3) {
synchronized (obj) {
try {
obj.notifyAll();
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("C");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
status = 1;
times--;
}
});
long start = System.currentTimeMillis();
thread1.start();
thread2.start();
thread3.start();
thread1.join();
thread2.join();
thread3.join();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
通过对比我们可以看出当线程需要运行长时间的时候使用wait和不使用wait进行循环的运行时间相差不多,但是从CPU使用率来看,Test2需要的CPU几乎是Test1的一半。
思考
1.当线程运行时间较短能较块释放锁时使用自旋锁需要的资源相对较少同时响应较块,当线程需要运行较长时间时,通过获取锁的方式让线程调用wait方法让线程进行等待,减少自旋时对资源的过度消耗。
2.java中synchronize对锁进行分级,分为无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,当调用的线程数增加时对锁进行升级。偏向锁:当只有一个线程访问该类时不进行加锁操作。轻量级:当有两个线程访问同一个类时,对锁进行升级,轻量级锁使用自选的方式进行等待。重量级锁:当多个线程访问同一个类时需要线程阻塞等待。java对锁的分级的原理也是类似,当两个线程访问同一个类时使用自选可以快速获得类的使用权,当多个线程访问同一个类时不能快速获得类的使用权,所以根据情况对锁进行升级,提高运行速度。