Java同步方法与同步语句的对比

来源:互联网 发布:折半查找的递归算法 编辑:程序博客网 时间:2024/06/16 06:21

众所周知,使用synchronized关键词实现同步的方式有两种:同步方法和同步语句。同步方法相对简单,但不够灵活;同步语句不仅能够避免在同步代码中调用其他对象的方法,从而减少死锁的可能性,而且能实现更细粒度的同步。

下面用程序来说明同步语句的两个优势。

1.避免在同步代码中调用其它对象的方法。考虑下面的程序:
public class DeadLock implements Runnable {    private Foo f1;    private Foo f2;        public DeadLock(Foo f1, Foo f2) {        this.f1 = f1;        this.f2 = f2;    }        public void run() {        synchronized(f1) {            try {                // 增加死锁的可能性                Thread.sleep(1000);            } catch (InterruptedException e) {            }            f2.bar();        }        // f2.bar();    }        public static void main(String[] args) {        Foo f1 = new Foo();        Foo f2 = new Foo();        new Thread(new DeadLock(f1, f2)).start();        new Thread(new DeadLock(f2, f1)).start();    }        private static class Foo {        public void bar() {            synchronized(this) {                            }        }    }}

程序运行后,就产生了一个死锁。这个死锁产生的原因是:在同步代码中调用了其他对象的方法。将f2.bar()移到同步代码之外,就可以避免这个死锁。使用同步方法对整个方法进行同步,显然不具备这种灵活性。

2.实现细粒度的同步

假设一个对象有两个域c1和c2,这两个域互不相关,为了提高并发度,我们需要对这两个域各自实现同步。也就是说,一个线程在访问c1时,另一个线程不能访问c1,但可以访问c2。细粒度同步通过减少不必要的阻塞,提高了并发度。下面的程序证明了这一点:

FineGrainedSynTest.java

public class FineGrainedSynTest {    public static void main(String[] args) throws Exception {        int numOfThreads = 100;        if (args.length > 0) {            numOfThreads = Integer.parseInt(args[0]);        }                NormalInc ni = new NormalInc();        FasterInc fi = new FasterInc();                Thread[] threads1 = new Thread[numOfThreads];        for (int i = 0; i < threads1.length; i++) {            threads1[i] = new Thread(new IncTask(ni));        }        ThreadsTimer tt1 = new ThreadsTimer(threads1);        tt1.start();                Thread[] threads2 = new Thread[numOfThreads];        for (int i = 0; i < threads2.length; i++) {            threads2[i] = new Thread(new IncTask(fi));        }        ThreadsTimer tt2 = new ThreadsTimer(threads2);        tt2.start();                System.out.format("普通并发对象耗时%dms,细粒度并发对象耗时%dms%n。", tt1.getDuration(), tt2.getDuration());                      }        private static class IncTask implements Runnable {        Inc inc;                public IncTask(Inc inc) {            this.inc = inc;        }                public void run() {            for (int i = 0; i < 100000; i++) {                inc.inc1();                inc.inc2();            }        }    }        private static interface Inc {        public void inc1();        public void inc2();    }        private static class NormalInc implements Inc {        private long c1 = 0;        private long c2 = 0;                public void inc1() {            synchronized(this) {                c1++;            }        }                public void inc2() {            synchronized(this) {                c2++;            }        }    }        private static class FasterInc implements Inc {        private long c1 = 0;        private long c2 = 0;        private Object lock1 = new Object();        private Object lock2 = new Object();                        public void inc1() {            synchronized(lock1) {                c1++;            }        }                public void inc2() {            synchronized(lock2) {                c2++;            }        }    }}

ThreadsTimer.java

import java.util.*;/* * ThreadsTimer用于统计一组线程从启动到结束的时间间隔 */public class ThreadsTimer {    private Thread[] threads; // 线程们    private long duration; // 运行时间        public ThreadsTimer(Thread[] threads) {        this.threads = threads;    }        public long getDuration() {        return duration;    }        /*     * 启动所有线程并开始计时。方法返回时,所有线程都已结束,且时间统计已完成。     */    public void start() throws InterruptedException {        long start = System.currentTimeMillis();                for (Thread thread:threads) {            thread.start();        }                join();                duration = System.currentTimeMillis() - start;        }        /*     * 当前线程频繁让出CPU时间片给待统计的线程,直到所有待统计的线程结束,从而使得统计更加准确     */    private void join() throws InterruptedException {        ArrayList<Thread> liveThreads = new ArrayList<>(threads.length);        for (Thread thread:threads) {            if (thread.isAlive()) {                liveThreads.add(thread);            }        }                while (!liveThreads.isEmpty()) {            // 随机选择一个存活线程,将时间片让给它            Random random = new Random();            int i = random.nextInt(liveThreads.size());            Thread t = liveThreads.get(i);                        if (t.isAlive()) {                t.join(100);            } else {                liveThreads.remove(i);            }        }    }    }

程序的几次运行结果如下:
D:\Java\多线程性能测试>java FineGrainedSynTest 5
普通并发对象耗时259ms,细粒度并发对象耗时187ms

D:\Java\多线程性能测试>java FineGrainedSynTest 10
普通并发对象耗时516ms,细粒度并发对象耗时382ms




0 0
原创粉丝点击