随笔:多线程的碎碎念
这次举一个例子来记录如何使用多个锁来加快程序运行速度,下面是基本的程序:
class Worker {
private int count1 = 0;
private int count2 = 0;
public void stageOne() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " count1: " + (count1++));
}
public void stageTwo() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " count2: " + (count2++));
}
public void process() {
for (int i = 0; i < 100; ++i) {
stageOne();
stageTwo();
}
}
public void main() {
System.out.println("Starting ... ");
long start = System.currentTimeMillis();
process();
long end = System.currentTimeMillis();
System.out.println("time take: " + (end - start));
System.out.println("count1: " + count1 + "; count2: " + count2);
}
}
public class MultipleLocksTest {
public static void main(String[] args) {
new Worker().main();
}
}
程序大概的意思是在Worker类中有两个变量count1
和count2
,分别在stageOne()
和stageTwo()
中增加一,并输出此时是哪个线程增加的。process()
迭代100次,每次都执行stageOne()
和stageTwo()
。上面的基本程序不是多线程程序,所以执行过程如下图所示:
最终结果为:
此时main函数是唯一的线程,以此执行stageOne()
和stageTwo()
。
现在我们加上两个线程,代码如下:
public void main() {
System.out.println("Starting ... ");
long start = System.currentTimeMillis();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
process();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
process();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("time take: " + (end - start));
System.out.println("count1: " + count1 + "; count2: " + count2);
}
现在有两个线程t1和t2,将会访问共享的count1,count2,我们现在没有进行任何的同步代码,所以执行过程如下:
执行的结果如下:
Thread-0和Thread-1将能够同时访问count1,count2,导致最终的结果不等于200。
现在我们分别给stageOne()
和stageTwo()
加上synchronized同步语句,那么执行的过程如下:
执行的结果如下:
因为加了同步,确保了每次只有一个线程能够访问count1和count2, 所以执行时间比没有加同步的多了1倍。
但是这种情况下,如果Thread-0获得了Worker对象的内部锁,在执行stageOne()
过程中,Thread-1不能执行stageOne()
也不能执行stageTwo()
,只有执行万stageOne()
或者stageTwo()
时,线程会释放内部锁,其他线程才能执行stageOne()
或者stageTwo()
,这也导致了在同一只能执行一个方法,要么stageOne()
,要么stageTwo
。因为这里两个方法写的是不同的变量,互相之间是独立的,所以我们可以这么修改程序,使执行速度加快:
private Object lock1 = new Object();
private Object lock2 = new Object();
public void stageOne() {
synchronized (lock1) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " count1: " + (count1++));
}
}
public void stageTwo() {
synchronized (lock2) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " count2: " + (count2++));
}
}
现在的执行过程如下所示:
执行结果如下:
可见,现在Thread-0在执行stageOne()
时,Thread-1可以执行stageTwo()
,最后的执行时间也少了1倍。