java synchronized详解--synchronized代码块

synchronized详解

回想起来,代码中一直用synchronized了,但是好像对它理解不到位,今天抓点时间复习一下。写了几个代码例子说明一下。

结构简述:

定义了一个资源PrintDemo,一个线程类ThreadDemo,一个测试类TestThread,在测试类中创建两个线程对象,两个线程对象都通过run方法访问资源PrintDemo.printCount方法

第一种场景:不对资源加锁

package com.yy.test;

/**
 * Created by skyler on 2017/2/17.
 */
class PrintDemo {
    public void printCount() {
        try {
            for(int i = 10; i > 0; i--) {
                System.out.println("Counter   ---   "  + i );
            }
        }catch (Exception e) {
            System.out.println("Thread  interrupted.");
        }
    }
}

class ThreadDemo extends Thread {
    private Thread t;
    private String threadName;
    PrintDemo  PD;

    ThreadDemo( String name,  PrintDemo pd) {
        threadName = name;
        PD = pd;
    }

    public void run() {
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        PD.printCount();
        System.out.println("Thread " +  threadName + " exiting.");
    }

    public void start () {
        System.out.println("Starting " +  threadName );
        if (t == null) {
            t = new Thread (this, threadName);
            t.start ();
        }
    }
}

public class TestThread {
    public static void main(String args[]) {
       TestThread tt = new TestThread();
       tt.test1();
    }
    public void test1() {
        PrintDemo2 PD = new PrintDemo2();

        ThreadDemo2 T1 = new ThreadDemo2( "Thread - 1 ", PD );
        ThreadDemo2 T2 = new ThreadDemo2( "Thread - 2 ", PD );

        T1.start();
        T2.start();

        // wait for threads to end
        try {
            T1.join();
            T2.join();
        }catch( Exception e) {
            System.out.println("Interrupted");
        }
    }
}

运行结果:
Starting Thread - 1 
Starting Thread - 2 
Counter   ---   10
Counter   ---   10
Counter   ---   9
Counter   ---   9
Counter   ---   8
Counter   ---   8
Counter   ---   7
Counter   ---   6
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.
Counter   ---   7
Counter   ---   6
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.

从结果看出,两个线程交互执行,也就是说,两个线程在抢占PrintDemo2.printCount()资源

第二种场景:资源加锁–>两个锁对象

说明:此场景为PrintDemo2.printCount()资源加入synchronized代码中,且两个线程各自传一个PrintDemo2对象实例

package com.yy.test;

/**
 * Created by skyler on 2017/2/17.
 */
class PrintDemo2 {
    public void printCount() {
        try {
            for(int i = 10; i > 0; i--) {
                System.out.println("Counter   ---   "  + i );
            }
        }catch (Exception e) {
            System.out.println("Thread  interrupted.");
        }
    }
}

class ThreadDemo2 extends Thread {
    private Thread t;
    private String threadName;
    PrintDemo2  PD;

    ThreadDemo2( String name,  PrintDemo2 pd) {
        threadName = name;
        PD = pd;
    }

    public void run() {
        synchronized (PD) {
            System.out.println(PD.toString());
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            PD.printCount();
            System.out.println("Thread " +  threadName + " exiting.");
        }
    }

    public void start () {
        System.out.println("Starting " +  threadName );
        if (t == null) {
            t = new Thread (this, threadName);
            t.start ();
        }
    }
}

public class TestThread {
    public static void main(String args[]) {
        TestThread tt = new TestThread();

        //两个线程不同的对象锁
        tt.test1();

        //两个线程同一个对象锁
        //tt.test2();
    }

    public void test1() {
        PrintDemo2 PD = new PrintDemo2();
        PrintDemo2 PD2 = new PrintDemo2();

        ThreadDemo2 T1 = new ThreadDemo2( "Thread - 1 ", PD );
        ThreadDemo2 T2 = new ThreadDemo2( "Thread - 2 ", PD2 );

        T1.start();
        T2.start();

        // wait for threads to end
        try {
            T1.join();
            T2.join();
        }catch( Exception e) {
            System.out.println("Interrupted");
        }
    }

    public void test2() {
        PrintDemo2 PD = new PrintDemo2();

        ThreadDemo2 T1 = new ThreadDemo2( "Thread - 1 ", PD );
        ThreadDemo2 T2 = new ThreadDemo2( "Thread - 2 ", PD );

        T1.start();
        T2.start();

        // wait for threads to end
        try {
            T1.join();
            T2.join();
        }catch( Exception e) {
            System.out.println("Interrupted");
        }
    }
}

这段代码与场景一代码大致相同,不同点有两处:

  1. ThreadDemo2.run方法加了synchronized (PD)锁,也就是对资源PrintDemo2.printCount()加锁

  2. TestThread.test1方法又增加了一个PrintDemo2对象PD2,并把他作为ThreadDemo2线程的锁的对象

运行结果:
Starting Thread - 1 
Starting Thread - 2 
com.yy.test.PrintDemo2@8005263
com.yy.test.PrintDemo2@5a28dc04
Counter   ---   10
Counter   ---   10
Counter   ---   9
Counter   ---   8
Counter   ---   9
Counter   ---   8
Counter   ---   7
Counter   ---   6
Counter   ---   7
Counter   ---   6
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.

从结果看出,两个线程加了锁的代码还是交替执行,说明锁没有起作用,这是为什么呢,别急,我们接着往下看

第三种场景:资源加锁,同一个锁对象

说明:次场景为PrintDemo2.printCount()资源加入synchronized代码中,且两个线程传同一个PrintDemo2对象实例

这时,TestThread类增加一个方法test2,这个方法与test1方法唯一不同的地方:把PD作为ThreadDemo2线程的锁的对象,也就是和ThreadDemo1的对象锁是一个了。注释掉main方法中test1方法,增加test2方法的调用。

查看结果:
Starting Thread - 1 
Starting Thread - 2 
com.yy.test.PrintDemo2@182c466e
Counter   ---   10
Counter   ---   9
Counter   ---   8
Counter   ---   7
Counter   ---   6
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 2  exiting.
com.yy.test.PrintDemo2@182c466e
Counter   ---   10
Counter   ---   9
Counter   ---   8
Counter   ---   7
Counter   ---   6
Counter   ---   5
Counter   ---   4
Counter   ---   3
Counter   ---   2
Counter   ---   1
Thread Thread - 1  exiting.

从结果看出,加锁的代码在一个线程执行完才在下一个线程执行,这说明锁起作用了

问题:

为什么场景二和场景三都给资源加了锁,场景二的锁没有作用而场景三起作用了呢?

原因:

从场景二和场景三的代码中我们能获得原因:多个线程访问同一资源时,要想保证一个资源对一个线程独享,资源不仅要加synchrozized锁,而且传给synchrozized锁(类似现实中的锁)对所有线程都是相同的(同一个)。

由此我们可以解释场景二锁没起作用的原因了:两个线程,线程一ThreadDemo1给资源的锁(类似现实中的锁)是PD,而线程二ThreadDemo2给资源的锁(类似现实中的锁)是PD2,资源的锁(类似现实中的锁)不是同一个,所以锁不起作用。

结论

在实际代码开发中,运用synchronized时,要注意锁(类似现实中的锁)的使用

以上说了一下synchronized代码块的使用。我们知道,synchronized还可以修饰方法,如:public synchronized static void get(),这时synchronized没有锁(类似现实中的锁),但是我们刚刚说了,没有锁的synchronized是不能正确给资源加锁的。java是支持synchronized方法的,那么synchronized方法怎么使用的呢。下一章我们接着说

以上说了synchronized应用级的用法,关于内部原理实现,我的水平还有待提高,请看下面关于原理的讲解:

http://www.importnew.com/23511.html
https://www.quora.com/How-does-synchronize-work-in-Java
http://blog.takipi.com/5-things-you-didnt-know-about-synchronization-in-java-and-scala/
http://gee.cs.oswego.edu/dl/cpj/jmm.html

你可能感兴趣的:(java基础,java,线程,synchroniz)