`

java Thread & synchronized & concurrent 线程、同步、并发

阅读更多
锁永远是锁的对象!即使对加在static method方法上的所谓“锁住的是当前类”的锁,其实锁的也无非是Class的实例对象。

线程与 JVM 退出的关系:
http://docs.oracle.com/javase/6/docs/api/java/lang/Thread.html
引用
When a Java Virtual Machine starts up, there is usually a single non-daemon thread (which typically calls the method named main of some designated class). The Java Virtual Machine continues to execute threads until either of the following occurs:
1. The exit method of class Runtime has been called and the security manager has permitted the exit operation to take place. (即调用System.exit())
2. All threads that are not daemon threads have died, either by returning from the call to the run method or by throwing an exception that propagates beyond the run method.


read it:
Chapter 20 of Inside the Java Virtual Machine - Thread Synchronization
http://www.artima.com/insidejvm/ed2/threadsynch.html
JLS Chapter 17. Threads and Locks
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.1



JDK1.5 concurrent 并发包专开:
http://wuaner.iteye.com/blog/1709915



The Java Tutorials -> Essential Java Classes -> Concurrency :
http://download.oracle.com/javase/tutorial/essential/concurrency/index.html
引用

Synchronized Methods章节:

The Java programming language provides two basic synchronization idioms: synchronized methods and synchronized statements

Synchronized methods enable a simple strategy for preventing thread interference and memory consistency errors: if an object is visible to more than one thread, all reads or writes to that object's variables are done through synchronized methods. (An important exception: final fields, which cannot be modified after the object is constructed, can be safely read through non-synchronized methods, once the object is constructed)

Intrinsic Locks and Synchronization章节:
Every object has an intrinsic lock(The API specification often refers to this entity simply as a "monitor.",即一些文章中常说的“同步监视器”) associated with it. By convention, a thread that needs exclusive and consistent access to an object's fields has to(必须) acquire the object's intrinsic lock before accessing them, and then release the intrinsic lock when it's done with them. A thread is said to own the intrinsic lock between the time it has acquired the lock and released the lock. As long as a thread owns an intrinsic lock, no other thread can acquire the same lock. The other thread will block when it attempts to acquire the lock.

When a thread releases an intrinsic lock, a happens-before relationship is established between that action and any subsequent acquistion of the same lock.

When a thread invokes a synchronized method, it automatically acquires the intrinsic lock for that method's object and releases it when the method returns. The lock release occurs even if the return was caused by an uncaught exception.

You might wonder what happens when a static synchronized method is invoked, since a static method is associated with a class, not an object. In this case, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class's static fields is controlled by a lock that's distinct from the lock for any instance of the class.

Another way to create synchronized code is with synchronized statements. Unlike synchronized methods, synchronized statements must specify the object that provides the intrinsic lock:
public void addName(String name) {
    synchronized(this) {
        lastName = name;
        nameCount++;
    }
    nameList.add(name);
}

In this example, the addName method needs to synchronize changes to lastName and nameCount, but also needs to avoid synchronizing invocations of other objects' methods. (Invoking other objects' methods from synchronized code can create problems that are described in the section on Liveness.)切记这一点:同步代码块中只应该包含需同步的当前对象的fields,而不要去同步其他对象的方法调用(如这里的list类型成员变量nameList的add()方法),否则容易产生死锁等问题)Without synchronized statements, there would have to be a separate, unsynchronized method for the sole purpose of invoking nameList.add.

Synchronized statements are also useful for improving concurrency(并发) with fine-grained(细粒度) synchronization. Suppose, for example, class MsLunch has two instance fields, c1 and c2, that are never used together. All updates of these fields must be synchronized, but there's no reason to prevent an update of c1 from being interleaved with an update of c2 — and doing so reduces concurrency by creating unnecessary blocking. Instead of using synchronized methods or otherwise using the lock associated with this, we create two objects solely to provide locks.(专门用了两个作为成员变量的对象lock1和lock2,来实现分别对c1和c2的细粒度同步!经典啊!!!)
public class MsLunch {
    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++;
        }
    }
}


关于上链接中多次提及的 happens-before relationship
http://dirkxu.iteye.com/blog/846922
引用
the result of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation.

http://www.iteye.com/problems/54095
引用
happens-before 是这样定义的,如果B能够看到A动作产生的结果,我们说A happens-before B

Program order rule. Each action in a thread happens-before every action in that thread that comes later in the program order. 同一个线程中,书写在前面的操作happen-before书写在后面的操作。



Java Language Specification -> 14.18 The synchronized Statement::
http://java.sun.com/docs/books/jls/second_edition/html/statements.doc.html#255769
Java Language Specification -> CHAPTER 17 Threads and Locks::
http://java.sun.com/docs/books/jls/second_edition/html/memory.doc.html#30206


The Java Virtual Machine Specification -> CHAPTER 8-Threads and Locks:
http://java.sun.com/docs/books/jvms/second_edition/html/Threads.doc.html


java中两种实现多线程的方式:
继承Thread类
实现Runnable接口


节选JDK Object notify()方法部分:
引用
A thread becomes the owner of the object's monitor in one of three ways:
    * By executing a synchronized instance method of that object.
    * By executing the body of a synchronized statement that synchronizes on the object.(补充一下:这里的object如果是Class类型的,则和下面一条一样,得到的是类级锁;具体怎么得到Class的实例,参见http://wuaner.iteye.com/blog/1009134
    * For objects of type Class, by executing a synchronized static method of that class.

Only one thread at a time can own an object's monitor.



线程状态:
http://download.oracle.com/javase/1.5.0/docs/api/java/lang/Thread.State.html
引用
public static enum Thread.State
extends Enum<Thread.State>
A thread state. A thread can be in one of the following states:

NEW
A thread that has not yet started is in this state.
RUNNABLE
A thread executing in the Java virtual machine is in this state.
BLOCKED
A thread that is blocked waiting for a monitor lock is in this state.
WAITING
A thread that is waiting indefinitely for another thread to perform a particular action is in this state.
TIMED_WAITING
A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state.
TERMINATED
A thread that has exited is in this state.
A thread can be in only one state at a given point in time. These states are virtual machine states which do not reflect any operating system thread states.
http://geekexplains.blogspot.com/2008/07/threadstate-in-java-blocked-vs-waiting.html
VisualVM 中看到的 threads,有 Running / Sleeping / Wait / Monitor 四种,其和 Thread.State 中定义的线程状态的关系是:
http://stringstream.blogspot.com/2011/11/thread-states-in-visualvm-profiler.html
引用
Running - RUNNABLE
Sleeping - TIMED_WAITING
Wait - WAITING
Monitor - BLOCKED



synchronized两种实现:synchronized methods 和 synchronized statements
  同步方法(被同步的方法不能是抽象类中的抽象方法和接口中的接口方法):
     非静态方法
引用
When a thread invokes a synchronized method(我的补充:非static), it automatically acquires the intrinsic lock for that method's object and releases it when the method returns
当一个线程调用一个synchronized(非static)方法时,自动获得这个方法的对象(即this对象)的monitor。

     静态方法
引用
Since a static method is associated with a class, not an object. So when a static synchronized method is invoked, the thread acquires the intrinsic lock for the Class object associated with the class. Thus access to class's static fields is controlled by a lock that's distinct from the lock for any instance of the class.
当一个线程调用一个static synchronized方法时,自动获得方法所在类对应的Class实例对象的monitor。

  同步块
synchronized(obj) {
        obj.wait();
        obj.notify(); 
        ...
    }



Exploring Java: Chapter 6, Threads (里头一个关于生产者消费者问题的例子非常的好):
http://oreilly.com/catalog/expjava/excerpt/index.html




关于 Daemon Thread:
The Java Virtual Machine exits when the only threads running are all daemon threads.
http://journals.ecs.soton.ac.uk/java/tutorial/java/threads/daemon.html
引用
Any Java thread can be a daemon thread. Daemon threads are service providers for other threads running in the same process as the daemon thread. For example, the HotJava browser uses up to four daemon threads named "Image Fetcher" to fetch images from the file system or network for any thread that needs one. The run() method for a daemon thread is typically an infinite loop that waits for a service request.
When the only remaining threads in a process are daemon threads, the interpreter exits. This makes sense because when only daemon threads remain, there is no other thread for which a daemon thread can provide a service.

To specify that a thread is a daemon thread, call the setDaemon method with the argument true. To determine if a thread is a daemon thread, use the accessor method isDaemon.

http://www.jguru.com/faq/view.jsp?EID=43724
引用
The core difference between user threads and daemon threads is that the JVM will only shut down a program when all user threads have terminated. Daemon threads are terminated by the JVM when there are no longer any user threads running, including the main thread of execution. Use daemons as the minions they are.
[In short: daemon threads do not keep the program from quitting; user threads keep the program from quitting. -Alex]

In java we have two type of Threads : Daemon Thread and User Threads. Generally all threads created by programmer are user thread (unless you specify it to be daemon or your parent thread is a daemon thread). User thread are generally meant to run our programm code. JVM doesn't terminates unless all the user thread terminate.
On the other hand we have Daemon threads. Typically these threads are service provider threads. They should not be used to run your program code but some system code. These thread run paralley to your code but survive on the mercy of the JVM. When JVM finds no user threads it stops and all daemon thread terminate instantly. Thus one should never rely on daemon code to perform any program code.
For better understanding consider a well known example of Daemon thread : Java garbage collector. Garbage collector runs as a daemon thread to recalim any unused memory. When all user threads terminates, JVM may stop and garbage collector also terminates instantly.


How can I create a daemon thread?
http://www.jguru.com/faq/view.jsp?EID=1251699
引用
The following is a simple program which creates a thread within the context of the main thread of execution. Since the main thread is a user thread the created thread will also be a user thread unless it's status is set otherwise. A couple of notes about the following code. If the created threads status is not set or false is passed to the setDameon() method then the created thread will fully execute. This is because the main thread cannot return until all non-daemon threads are finished. Setting the status to true in our case doesn't give our daemon thread much time to do it's bussiness since the main method will quickly execute then return thus stopping our daemon thread dead in it's tracks. We would be lucky to get a print out of one year!. So what can we do? Either we can put the main thread to sleep for an amount of time (enough to give our dameon thread time to do it's business) or simply make it a non-daemon thread by setting it's status to false.
public class DaemonThreadTest {

    public static void main(String[] args) {
        Thread t = new Thread(new DaemonThread(525.00f, 0.09f));
        t.setDaemon(true);
        t.start();
//        try {
//            Thread.sleep(250); // must give the deamon thread a chance to run! 
//        } catch (InterruptedException ei) { // 250 mills might not be enough!
//            System.err.println(ei);
//        }
    }
}

class DaemonThread implements Runnable {
    
    private float principal;
    private float futureval;
    private float rate;
    private float i;

    public DaemonThread(float principal, float rate) {
        this.principal = principal;
        this.rate = rate;
    }

    public void run() {
        for (int year = 0; year <= 1000; year++) {
            i = (1 + rate);
            futureval = principal * (float) Math.pow(i, year);
            System.out.print(principal + " compounded after " + year
                    + " years @ " + rate + "% = " + futureval);
            System.out.println();
        }
    }
}

这里例子很好的说明了daemon thread(附件里有):当线程t不是守护线程时,其输出结果是完整的1001行输出;当其是守护线程时,具体会输出多少行就无法预知了。




Thread范例:
http://www.java2s.com/Code/Java/Threads/CatalogThreads.htm


'Main' Thread in Java:
http://www.go4expert.com/forums/showthread.php?t=4178


java线程安全总结:
http://www.iteye.com/topic/806990
线程安全总结(二):
http://www.iteye.com/topic/808550
线程总结:
http://www.iteye.com/topic/768909
线程同步:
http://www.iteye.com/topic/164905
多线程,不得不说:
http://www.iteye.com/topic/158377
更好的把握线程<一>:Thread (线程)介绍:
http://www.iteye.com/topic/202329


java.util.concurrent:
http://download.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/package-summary.html
http://doc.java.sun.com/DocWeb/api/java.util.concurrent
Concurrent Programming with J2SE 5.0:
http://java.sun.com/developer/technicalArticles/J2SE/concurrency/
Java Concurrency / Multithreading:
http://www.vogella.de/articles/JavaConcurrency/article.html

实战Concurrent:
http://www.iteye.com/topic/363625
java并发编程-Executor框架:
http://www.iteye.com/topic/366591
Java多线程--让主线程等待所有子线程执行完毕:
http://www.iteye.com/topic/581476?page=2
淘宝面试题:如何充分利用多核CPU,计算很大的List中所有整数的和:
http://www.iteye.com/topic/711162


wait和sleep的区别:
Object的wait()方法的调用方是进入当前代码块的线程所锁定的对象!Thread的静态方法sleep()的调用方是进入当前代码块的线程!
wait释放锁,使得其他线程可以使用同步控制块或者方法;sleep方法不会释放锁;
wait方法属于Object;sleep方法属于Thread;
因为一个对象的wait(也包括notify/notifyAll)方法只能被拥有这个对象的monitor的线程所调用,所以wait方法需要和synchronized关键字配合使用,而sleep可以在任何地方使用;

关于他们的区别,网上还有一段话是这样说的:
“sleep必须捕获异常,而wait,notify和notifyAll不需要捕获异常”
这句话纯属扯淡。作为一个会抛出checked exception的方法,调用wait()方法岂可不对异常做处理!这句话想表述的道理其实是这样的:
调用sleep,原则上该用try-catch[-finally]语句块来捕获InterruptedException;而调用wait方法(以及notify和notifyAll),处理InterruptedException时可以捕获它,也可以继续throws它。

为什么外围不加synchronized的wait()方法调用会报“current thread not owner”
原因很简单:单独的一条语句 wait(); = this.wait(); JDK有规定:对于Object的wait()、wait(long timeout)、wait(long timeout,int nanos)、notify()、notifyAll()这五个方法,should only be called by a thread that is the owner of this object's monitor。不加synchronized的话,则你无法保证执行到obj.wait()(或者notify()、notifyAll()等)这条语句的线程是拥有obj对象的线程锁(即monitor 同步监视器)的,wait调用无从谈起!

所以,synchronized锁了哪个对象,则拥有该对象锁的线程就可以调用该对象的wait()(以及notify()等)。如上面的这段代码:
public class MsLunch {
    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++;
        }
    }
}

则这里的wait()调用是这样的:
public class MsLunch {
    private long c1 = 0;
    private long c2 = 0;
    private Object lock1 = new Object();
    private Object lock2 = new Object();

    public void inc1() {
        synchronized(lock1) {
            lock1.wait();
            // this.wait(); //这里不可调this.wait();因为进入这段同步代码块的线程并没有拥有当前MsLunch实例对象的monitor!!!
            c1++;
        }
    }

    public void inc2() {
        synchronized(lock2) {
            lock2.wait();
            // this.wait(); //这里不可调this.wait();因为进入这段同步代码块的线程并没有拥有当前MsLunch实例对象的monitor!!!
            c2++;
        }
    }
}
http://www.coderanch.com/t/232016/threads/java/Current-thread-not-owner
引用
synchronized (lockedRecordMap) {  
  ...
  wait(); //Error!because:you don't own the this object, so how dare you tell me what to do with it??!!
  ...
}

When you say myObject.wait() , what you're saying to the JVM is "mr. JVM, please stop executing the current Thread until myObject is unlocked".
But that's not what your (current) code block is saying: Since you're just calling wait() , you are, in fact, calling this.wait() . That is, you're calling wait on the this object.
You're saying to the JVM
"mr. JVM, please stop executing the current Thread until this is unlocked".
And the JVM is responding back to you by saying
"mr. Programmer, you don't own the this object, so how dare you tell me what to do with it?".
And the JVM is right: you don't own the this object. Why not? Because you didn't synchronize on this , you synchronized on lockedRecordMap .
You could have done so by synchronizing the method or syychronizing explicitly on this , but I don't think you want to in this case.




Thread join()方法:
http://download.oracle.com/javase/tutorial/essential/concurrency/join.html
引用
The join method allows one thread to wait for the completion of another. If t is a Thread object whose thread is currently executing,
t.join();
causes the current thread to pause execution until t's thread terminates.

调用join()方法的线程t会pause当前线程!直到t线程terminated!
例子:
public class TestJoin {
    
    static void threadMessage(String message) {
        String threadName = Thread.currentThread().getName();
        System.out.format("%s: %s%n", threadName, message);
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new SimpleThread());
        thread.start();
        try {
            thread.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for(int i=0; i<9; i++) {
            threadMessage("i am running ...");
        }
    }
    
    private static class SimpleThread implements Runnable {
        public void run() {
            for(int i=0; i<9; i++) {
                threadMessage("i am running ...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
输出:
引用
Thread-0: i am running ...(注:每一秒输出一次)
Thread-0: i am running ...(注:每一秒输出一次)
Thread-0: i am running ...(注:每一秒输出一次)
Thread-0: i am running ...(注:每一秒输出一次)
Thread-0: i am running ...(注:每一秒输出一次)
Thread-0: i am running ...(注:每一秒输出一次)
Thread-0: i am running ...(注:每一秒输出一次)
Thread-0: i am running ...(注:每一秒输出一次)
Thread-0: i am running ...(注:每一秒输出一次)
main: i am running ...
main: i am running ...
main: i am running ...
main: i am running ...
main: i am running ...
main: i am running ...
main: i am running ...
main: i am running ...
main: i am running ...




synchronized methods 例子:
/**
 *synchronized static 方法是为类加的锁;synchronized非static 方法是为对象加的锁。
 * 说明:
 * 当某一线程访问一个对象的synchronized非static方法时,其他线程不可以访问该对象的其他synchronized非static方法,可以访问其他的方法(包括普通的非synchronized方法,和static synchronized方法)
 * 当某一线程访问一个对象的static synchronized方法时,其他线程不可以访问该对象的其他static synchronized方法,可以访问其他的方法(包括普通的非synchronized方法,和synchronized非static方法)
 */
public class TestSync {

    public static void main(String[] args) {
        
        /**
         * 择一
         */
        Thread1 t = new Thread1();
        //Thread2 t = new Thread2();
        //Thread3 t = new Thread3();
        //Thread4 t = new Thread4();
        //Thread5 t = new Thread5();
        
        Thread thread = new Thread(t);
        thread.setName("sub");
        thread.start();
        try {
            Thread.sleep(500); //这句话是为了确保在main 线程执行t的方法前,sub线程已进入t的同步代码块
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        t.m2();
    }
}

/**
 * 
 * 输出:
Thread main 进入方法 m2(), i=1000
(大概五秒钟后...)
Thread sub 进入方法 m1(), i=1000
 */
class Thread1 implements Runnable {
    
    private int i = 10;
    
    public void run()  {
        m1();
    }
    
    synchronized public void m1(){
        i = 1000;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread " + Thread.currentThread().getName() + " 进入方法 m1(), i=" + i);
    }
    
    public void m2() {
        System.out.println("Thread " + Thread.currentThread().getName() + " 进入方法 m2(), i=" + i);
    }
}


/**
 * 
 * 输出:
(大概五秒钟后...)
Thread sub 进入方法 m1(), i=1000
Thread main 进入方法 m2(), i=1000
 */
class Thread2 implements Runnable {
    
    private int i = 10;
    
    public void run()  {
        m1();
    }
    
    synchronized public void m1(){
        i = 1000;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread " + Thread.currentThread().getName() + " 进入方法 m1(), i=" + i);
    }
    
    public synchronized void m2() {
        System.out.println("Thread " + Thread.currentThread().getName() + " 进入方法 m2(), i=" + i);
    }
}

/**
 * 输出:
Thread main 进入方法 m2(), i=1000
(大概五秒钟后...)
Thread sub 进入方法 m1(), i=1000
 */
class Thread3 implements Runnable {

    private static int i = 10;
    
    public void run()  {
        m1();
    }
    
    public synchronized static void m1() {
        i = 1000;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread " + Thread.currentThread().getName() + " 进入方法 m1(), i=" + i);
    }
    
    public synchronized void m2() {
        System.out.println("Thread " + Thread.currentThread().getName() + " 进入方法 m2(), i=" + i);
    }
}

/**
 * 输出:
(大概五秒钟后...)
Thread sub 进入方法 m1(), i=1000
Thread main 进入方法 m2(), i=1000
 */
class Thread4 implements Runnable {

    private static int i = 10;
    
    public void run()  {
        m1();
    }
    
    public synchronized static void m1() {
        i = 1000;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread " + Thread.currentThread().getName() + " 进入方法 m1(), i=" + i);
    }
    
    public static synchronized  void m2() {
        System.out.println("Thread " + Thread.currentThread().getName() + " 进入方法 m2(), i=" + i);
    }
}

/**
 * 输出:
Thread main 进入方法 m2(), i=1000
(大概五秒钟后...)
Thread sub 进入方法 m1(), i=1000
 */
class Thread5 implements Runnable {

    private static int i = 10;
    
    public void run()  {
        m1();
    }
    
    public synchronized void m1() {
        i = 1000;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Thread " + Thread.currentThread().getName() + " 进入方法 m1(), i=" + i);
    }
    
    public synchronized static void m2() {
        System.out.println("Thread " + Thread.currentThread().getName() + " 进入方法 m2(), i=" + i);
    }
}


结合一个面试题深入剖析下synchronized statements 的锁机制:
该面试题见:
http://wuaner.iteye.com/admin/blogs/567792
这里仅对该面试题的正确答案做下分析,错误答案不再赘述。
附件中的Letters.rar即为下面的代码。
public class Letters extends Thread {
    
    private String name;

    public Letters(String name) {
        this.name = name;
    }

    public void write() {
        System.out.print(Thread.currentThread().getName() + name);
        System.out.print(Thread.currentThread().getName() + name);
    }

    public static void main(String[] args) {
        Letters l1 = new Letters("X");
        Letters l2 = new Letters("Y");
        l1.start();
        l2.start();
        try {
            Thread.sleep(500); //这句话是为了确保在main方法执行l2的write方法前,两个sub线程已进入Letters的同步代码块
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        l2.write();
        
//        synchronized(System.out) { //不加这行会报“current thread not owner”
//            System.out.println("main 线程得到了 System.out 的锁");
//            System.out.notify(); //替换成notifyAll()效果会不一样。
//        }
    }
    
    public void run() { 
        synchronized(Letters.class) { 
            try {
                Thread.sleep(5000);
                ///System.out.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            write(); 
        }
    } //OK
    
    /**
     * 
     * 如果锁的是Letters.class,输出可能为:
     * 1 mainYmainY(大概五秒钟后..)Thread-0XThread-0X(大概五秒钟后..)Thread-1YThread-1Y
     * 2 mainYmainY(大概五秒钟后..)Thread-1YThread-1Y(大概五秒钟后..)Thread-0XThread-0X
     * 这里想说明的是:
     * main线程对非同步方法write()的调用并没有因为Thread-0(or Thread-1)持有了Letters的类锁而受影响,说明了:
     * 当一个线程持有一个类的类锁(即这个类对应的Class实例的锁)的时候,其他线程可以访问该类的非同步方法(也可以访问该类的synchronized非static方法,但不可以访问该类的static synchronized方法)
     * 
     * 
     * 如果锁的是System.out,输出可能为:
     * 1 (大概五秒钟后..)Thread-1YThread-1Y(大概五秒钟后..)Thread-0XThread-0XmainYmainY
     * 2 (大概五秒钟后..)Thread-0XThread-0X(大概五秒钟后..)Thread-1YThread-1YmainYmainY
     * 这里想说明的是:
     * System.out这个对象是静态的,只有一份,所以这里可以通过对这个对象加锁实现题目要求;
     * 但为什么main线程对Letters的非同步方法write()的调用会受到Thread-0(or Thread-1)的影响那(被阻塞)?原因在于write方法中调用了System.out.print!详细解释如下:
     * first,看JDK中的几条东西:
     * *System类中:public final static PrintStream out = nullPrintStream(); //注意该out变量是static的,所以只会有一份
     * *PrintStream类中:
     *      public void print(String s) {
     *          if (s == null) {
     *              s = "null";
     *          }
     *          write(s);
     *      }
     *      private void write(String s) {
     *          try {
     *              synchronized (this) {
     *              ensureOpen();
     *              textOut.write(s);
     *              textOut.flushBuffer();
     *              charOut.flushBuffer();
     *              if (autoFlush && (s.indexOf('\n') >= 0))
     *                  out.flush();
     *              }
     *          }
     *          catch (InterruptedIOException x) {
     *              Thread.currentThread().interrupt();
     *          }
     *          catch (IOException x) {
     *              trouble = true;
     *          }
     *      }
     * PrintStream类的write()方法中的synchronized (this)正是造成main线程被阻塞的原因所在!详析如下:
     * main线程没有任何阻碍的到达了Letters的非同步方法write()的内部;当在该方法内部调用
     * System.out.print(),继而调用PrintStream.write()时,main线程试图锁住this(这里的this,即是那独有一份的System静态成员变量System.out)
     * 结果很不幸,System.out已经被两个子线程中的其中一个提前锁定了!所以,main线程被阻塞。
     * 
     * 
     * 扩展开来:
     * 只要在不同线程进来时,能保证这里要锁定的对象是同一个对象,就可以满足题目的要求!
     * 举几个例子,锁定以下对象都是可以保证结果符合题目要求的:
     * synchronized(new Letters("").getClass()) , 这个和锁 Letters.class 是完全一样的,都是得到Letters这个类的类级锁。
     * synchronized(Class.forName("Letters")) 这个和锁 Letters.class 也是一样的。
     * synchronized("abc") "abc" 仅指向常量池中独有的那一份,故这样做可以。
     * synchronized(int.class) int.class在jvm中也是唯一的。
     * synchronized(System.out.getClass())
     * 
     * 
     * 这个题目在Letters的run()方法中调用Letters的非同步方法write(),给人一种假象,似乎对象加锁后对象所属类的非同步方法也无法访问了。
     * 其实根本就不是这么回事!千万别被这种假象迷惑。
     * 因为synchronized关键字是写在run方法内、write方法外,所以只有走run方法的线程(哪些线程会走run方法?当然是基于该线程实现类所start的线程对象们;当然,直接调用该run()方法的线程也会走),在访问write()方法时是无并发串行的来.
     * 而其他线程对Letters的非同步方法write()的调用不会受任何影响,正如上面“在锁Letters.class的时候main对write()的调用不受影响”的例子所展示的那样。
     * 
     * 
     * 如果去掉代码中的两行Thread.sleep():
     * 对于锁Letters.class的情况:main线程调用非同步方法write()产生的两个输出在最终输出串中的位置根本就是无法预知的;唯有两个子线程他们之间的输出是有序的(具体他俩谁在前那就不一定了).如:
     * Thread-0XmainYThread-0XmainYThread-1YThread-1Y
     * mainYThread-0XmainYThread-0XThread-1YThread-1Y
     * mainYThread-1YmainYThread-1YThread-0XThread-0X
     * Thread-0XmainYThread-0XThread-1YThread-1YmainY
     * Thread-0XmainYmainYThread-0XThread-1YThread-1Y
     * 对于锁System.out的情况:main线程调用非同步方法write()产生的两个输出在最终输出串中的位置仍然是无法预知的;两个子线程的输出也有上面的特点,但有一点与上面的不同:对于任一子线程,其产生的两个输出一定是紧挨着的!如:
     * Thread-0XThread-0XmainYThread-1YThread-1YmainY
     * mainYThread-0XThread-0XmainYThread-1YThread-1Y
     * Thread-0XThread-0XmainYThread-1YThread-1YmainY
     * mainYThread-1YThread-1YmainYThread-0XThread-0X
     * Thread-1YThread-1YThread-0XThread-0XmainYmainY
     * 
     * 
     * 这个例子说明的问题:
     * 如果一个线程的实现类的run()方法,内部有对同一对象实例(这里的同一对象,指的是不管进来的是哪个线程,这个对象永远不变)的synchronized()块(在run()方法本身的外围加上synchronized不行;因为这种方式锁定的是this对象,不会在所有线程进来时都是同一个),则基于该线程类所start的各个不同线程实例,就只能是串行的了!没有并发的可能性。
     * 
     * 
     * 代码中注释掉的是对wait()和notify()做测试的例子;还有待补充。
     */
}
  • 大小: 29 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics