今天上课的时候,在测试一段代码时想到了一点简单的保证线程安全的东西。
首先是待测试的代码:
<pre name="code" class="java">
public class ThreadCount {
public static void main(String[] args) {
Thread[] threads=new Thread[10000];
for (int i = 0; i < 10000; i++) {
threads[i]=new AThread();
threads[i].start();
}
}
}
class AThread extends Thread{
public void run(){
Counter counter = new Counter();
System.out.println(Counter.calNum());
}
}
class Counter{
private static long num;
public Counter(){
num++;
}
public static long calNum(){
return num;
}
}
很明显,这段代码中共享的临界资源就是Counter中num变量,在不同的线程中对num进行++操作,但是在输出的时候不能及时更新。
所以我们要处理的就是如何保持保证这个num的同步使用(只有在输出之后才允许其他线程使用Counter的构造方法)。
方法一:直接锁住Counter这个类(其实就是保证其中的static变量只能有一个线程在访问)
public class ThreadCount {
public static void main(String[] args) {
Thread[] threads=new Thread[10000];
for (int i = 0; i < 10000; i++) {
threads[i]=new AThread();
threads[i].start();
}
}
}
class AThread extends Thread{
public void run(){
synchronized (Counter.class) {
Counter counter = new Counter();
System.out.println(Counter.calNum());
}
}
}
class Counter{
private static long num;
public Counter(){
num++;
}
public static long calNum(){
return num;
}
}
方法二:(构造一个监视对象专用用来保证代码的连续运行,这一点在很多时候很有用;并且有了这个监视对象之后,可以灵活地对代码块进行同步),从下面的例子中我们可以看到,如果这些线程都公用一个ThreadMonitor则可以随时利用这个Object来对代码块进行锁定。
public class ThreadCount {
public static void main(String[] args) {
Object ThreadMonitor = new Object();
Thread[] threads=new Thread[10000];
for (int i = 0; i < 10000; i++) {
threads[i]=new AThread(ThreadMonitor);
threads[i].start();
}
}
}
class AThread extends Thread{
Object ThreadMonitor = null;
public AThread(Object ThreadMonitor){
this.ThreadMonitor = ThreadMonitor;
}
public void run(){
synchronized (ThreadMonitor) {
Counter counter = new Counter();
System.out.println(Counter.calNum());
}
}
}
class Counter{
private static long num;
public Counter(){
num++;
}
public static long calNum(){
return num;
}
}
方法三:其实这种方法和方法二没有什么区别,可以直接使用Lock对象来对代码块,进行锁定,但是由于是多个进程,所以要把Lock对象设为静态static变量,这样所有的线程都是共用一个Lock对象,从而形成互斥访问。
public class ThreadCount {
public static void main(String[] args) {
Thread[] threads=new Thread[10000];
for (int i = 0; i < 10000; i++) {
threads[i]=new AThread();
threads[i].start();
}
}
}
class AThread extends Thread{
private static Lock runlock = new ReentrantLock();
public void run(){
runlock.lock();
try{
Counter counter = new Counter();
System.out.println(Counter.calNum());
} finally{
runlock.unlock();
}
}
}
class Counter{
private static long num;
public Counter(){
num++;
}
public static long calNum(){
return num;
}
}
现在对多线程的理解还很幼稚,所以应该多想想如何使用。其实这里的方法二是一种很好的方式,对于临界资源的访问可以使用一个Monitor来监视,这样就能互斥访问。