回想起来,代码中一直用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");
}
}
}
这段代码与场景一代码大致相同,不同点有两处:
ThreadDemo2.run方法加了synchronized (PD)锁,也就是对资源PrintDemo2.printCount()加锁
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