java多线程 -- synchronized同步方法与同步块的区别

测试用例(1):使用synchronized方法实现简单售票系统

public class SaleTicket implements Runnable {

    public int count;

    public SaleTicket() {
        count = 30;
    }
    
    public synchronized void startP(){
        while (count > 0) {
//            synchronized (SaleTicket.class) {
                if (count > 0) {
                    try {
                        Thread.sleep(new Random().nextInt(1000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "\t当前票号:" + count--);
                }
//            }
        }
    }

    public void run() {
        startP();
    }

    public static void main(String[] args) {
        SaleTicket st = new SaleTicket();
        for (int i = 1; i <= 5; i++) {
            new Thread(st, "售票点:" + i).start();
        }
    }

}

运行结果为:

售票点:1    当前票号:30
售票点:1    当前票号:29
售票点:1    当前票号:28
售票点:1    当前票号:27
售票点:1    当前票号:26
售票点:1    当前票号:25
售票点:1    当前票号:24
售票点:1    当前票号:23
售票点:1    当前票号:22
售票点:1    当前票号:21
售票点:1    当前票号:20
售票点:1    当前票号:19
售票点:1    当前票号:18
售票点:1    当前票号:17
售票点:1    当前票号:16
售票点:1    当前票号:15
售票点:1    当前票号:14
售票点:1    当前票号:13
售票点:1    当前票号:12
售票点:1    当前票号:11
售票点:1    当前票号:10
售票点:1    当前票号:9
售票点:1    当前票号:8
售票点:1    当前票号:7
售票点:1    当前票号:6
售票点:1    当前票号:5
售票点:1    当前票号:4
售票点:1    当前票号:3
售票点:1    当前票号:2
售票点:1    当前票号:1
根据结果我们分析得出结论,synchronized应用到方法上,是得到了该对象(实例)的锁,对象(实例)锁特点是:哪个线程售票点获得该对象(实例)将会独自执行完毕(也就是独自持有"锁")所以售票点:1先得到了锁,所以一直是它把所有的票都卖完!

Synchronized 方法控制对类成员的访问:每个类实例对应一把锁,每个synchronized方法都必须获得调用该方法的实例的锁方能执行,否则所属线程阻塞,方法一旦 执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保同一时刻对于每一个实例,其所声明为 synchronized的成员函数中至多只有一个处于可执行的状态(因为至多只有一个能够获得该实例对应的锁),从而有效的避免了类成员变量的访问冲突。(多线程并发执行的时候只能有一个线程执行synchronized method(){代码})

测试用例(2):使用synchronized同步块儿实现简单售票系统

public class SaleTicket implements Runnable {

    public int count;

    public SaleTicket() {
        count = 30;
    }
    
    public /*synchronized*/ void startP(){
        while (count > 0) {
            synchronized (SaleTicket.class) {
                if (count > 0) {
                    try {
                        Thread.sleep(new Random().nextInt(1000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "\t当前票号:" + count--);
                }
            }
        }
    }

    public void run() {
        startP();
    }

    public static void main(String[] args) {
        SaleTicket st = new SaleTicket();
        for (int i = 1; i <= 5; i++) {
            new Thread(st, "售票点:" + i).start();
        }
    }

}

运行结果为:

售票点:1    当前票号:30
售票点:1    当前票号:29
售票点:1    当前票号:28
售票点:1    当前票号:27
售票点:1    当前票号:26
售票点:5    当前票号:25
售票点:5    当前票号:24
售票点:5    当前票号:23
售票点:5    当前票号:22
售票点:5    当前票号:21
售票点:5    当前票号:20
售票点:5    当前票号:19
售票点:5    当前票号:18
售票点:5    当前票号:17
售票点:5    当前票号:16
售票点:5    当前票号:15
售票点:5    当前票号:14
售票点:5    当前票号:13
售票点:5    当前票号:12
售票点:5    当前票号:11
售票点:5    当前票号:10
售票点:5    当前票号:9
售票点:5    当前票号:8
售票点:5    当前票号:7
售票点:5    当前票号:6
售票点:5    当前票号:5
售票点:5    当前票号:4
售票点:5    当前票号:3
售票点:5    当前票号:2
售票点:5    当前票号:1
根据结果我们分析得出结论,当两个并发线程访问同一个对象(实例)中的这个synchronized(SaleTicket.class) 当一个线程访问object的一个synchronized(SaleTicket.class)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块,所以线程“售票点:5”也有机会执行同步块。尤其关键的是,当一个线程访问object的一个synchronized(SaleTicket.class)同步代码块时,其他线程对object中所有其它synchronized(SaleTicket.class)同步代码块的访问将被阻塞,也就是说,当一个线程访问object的一个synchronized(SaleTicket.class)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。(这种同步块方式缩小了需要安全的地方,同时也加强了程序运行的效率)

猜你喜欢

转载自zliguo.iteye.com/blog/2249669