《Java Thread Programming》读书笔记

1.一个HelloWorld的java程序有几个线程,分别都是干什么的?如果带图形界面的呢?能在工作线程中创建图形窗口吗?如何实现用一个对话框去等待另一个线程结束?

答:是不是可以考虑通过线程函数枚举线程得出结果,确实可以这样,根据当前线程找到对应的线程组,然后一直往上找到根线程组,通过enumerate就可以找到所有的线程。

Reference Handler

Finalizer

Signal Dispatcher

Attach Listener

main

如上几个线程的作用只能从名字分析,没有找到官方的说明。

如果是带界面的Java Project程序,线程仍然和普通的Java Project一样多。

如果是RCP程序,则

Reference Handler

Finalizer

Signal Dispatcher

Attach Listener

main

State Data Manager

Start Level EventDispatcher

Framework EventDispatcher

Thread-1

红色的那些线程调试时是看不见的,其他的都可见,但是不知道有没有办法看看这些线程的源代码?

 

 

 

2.Java中多线程编程最容易的原因之一是当发生运行时异常时,系统会自动释放该线程拥有的锁。之二就是获得和释放一个对象的锁很容易,因为每个对象都是从Object继承的,而每个Object中都内建了一个锁。

 

3.从Thread继承是最简单的,实现Runnable接口在现实世界中工作得更好,因为Java不能多继承,要是从Thread继承了,就不能再从其他类继承了,在很多时候搞不定,所以只能采取实现Runnable接口。

 

4.Thread的代码是如何实现的,从书上看它是继承自Object,实现了Runnable接口,估计run实现为抽象函数了,所以子类还能改写。继承Thread和实现Runnable有何本质区别和联系?

 

5.currentThread()和C++中的GetCurrentThread一样,注意,一个类继承自Thread,并不代表所有成员函数中调用currentThread()都是返回this,一般仅仅在run函数中才是刚好和this相等。

 

6.尽管Java并没要求怎样,但当命名线程的时候遵循下面的习惯是好的:

1.在start()之前调用setName()

2.给每个线程一个唯一直观的名字

3.不要改变JavaVM线程(也就是虚拟机产生的线程)的名字,比如main

 

7.在Java中,一个线程组(非常像目录)能包含线程和其他的线程组。这些其他的线程组反过来又可以包含其他的线程和线程组。每个线程(非常像文件)是一个线程组的成员,这个线程组可能有时另一个线程组的成员。

如果创建线程时没有指定线程组,它就从创建它的线程继承线程组。

线程组有什么用?

 

8.如果C++中的Sleep一样,Java中的sleep永远都是针对当前线程。即针对Thread.currentThread。虽然你用一个线程对象去调用sleep,你以为是让那个线程sleep,那么你就错了,从sleep是一个静态函数就可以知道让某个线程sleep是不行的。(已验证)

 

9.为什么一般我们Runnable用得多,因为Java中不支持多继承,只能通过实现接口解决。

 

10.从RuntimeException继承的异常不强制try/catch它,它存在的目的是什么呢?

 

11.interrupt()可以中断sleep(),wait()等待,C++中好像是没有办法中断Sleep函数的。在interrupt时,如果被中断的线程正处在等待/睡眠状态,被中断的线程立刻抛出InterruptedException异常。如果没有处于等待状态,则简单地设置中断标记为真,当下次调用sleep,wait时,会立刻抛出InterruptedException异常。当捕获中断异常后,中断标记会立刻被重置。isinterrupted可以检查线程的中断状态,而interrupted在检查的同时还会重置状态。

 

12.在一个long-running方法中,可以考虑使用

if(Thread.interruped()){

   throw newInterruptedException();

}

去检查是否有中断,它是从一个线程去取消另一个线程的长时操作的好办法。

这和用普通的一个boolean变量去检查是否有取消有什么区别?

 

13.为什么会deprecate suspend()和resume?

从书上看,主要还是怕被暂停的线程刚好拥有锁,这样就容易造成死锁。C++中就可以武断些,把自由交给用户。书上还提供了一个比较简单的不使用suspend()去实现暂停的办法,大意就是在线程函数的安全地方加入

while(suspended){

   Thread.sleep(200);

}

在另一个线程中通过改变suspended的值达到暂停和恢复的效果。但这个办法也有弊端,sleep过长那么恢复不及时,过短,不断检查浪费CPU。跟直接调用suspend函数比起来,这样还增加了被暂停线程的实现复杂度,无法在任何一个语句暂停。书上说最好的办法是用wait()/notify()实现。

 

14.stop相当于C++中的TerminatedThread,所以在sun中也是deprecate了的。

 

15.书上说的快速停止一个线程的办法就是设置一个运行标识,如果线程中有sleep,则还需要调用interrupt中断一下sleep,免得要等着超时才能退出线程。

 

16.The Best Replace for stop(),suspend() andrusume()?

还要参考第17章的东西,放在后面看。

 

17.只要在启动线程之前先调用t.setDaemon(true);,这个线程就成为后台线程了。后台线程很贱,只要进程zh偶那个没有正常线程了,虚拟机就会直接把它们stop掉,这样连后台线程函数中的finally语句都得不到执行,而进程中只要有正常线程,进程就不会退出,差别之大啊!所以使用后台线程时一定要小心,因为它们的stop结束方式。

 

18.Java线程优先级最大值和最小值分别由Thread.MAX_PRIORITY和Thread.MIN_PRIORITY指定。

 

19.Java线程有6个状态:running,ready to run,sleeping,waiting, blocked I/O,blocked on sync。

这6个状态并不是官方定义的,但对明白线程的行为很重要。

sleep,超时或被中断可返回

wait,超时或中断或通知可返回

blocked I/O 直到有数据被读到才返回,没数据时我们如何让其返回呢?

blocked on sync 得到指定的锁可返回

 

20.优先级分配跟C++中一样,低优先级的线程仍然可以获得到点CPU时间。

 

21.虚拟机与虚拟机之间,操作系统与操作系统之间对于线程的调度算法都可以不一样。

 

Chapter 7: Concurrent Access to Objects andVariables

 

volatile Member Variable Modifier

In fact, many experienced Java developers don’tunderstand when the use of volatile is necessary.

我也是大概知道,以前在C++中没用它好像也没出现什么问题。

 

The Java Language Specification indicates that foroptimal speed, individual threads are permitted to keep a working copy ofshared member variables and only reconcile(v.使一致)them with the shared original occasionally. To be more accurate, the word “occasionally” in thelast sentence should be replaced with “when a thread enters or leavesa synchronized block of code.”

也就是只要一个线程执行了一个synchronized,哪怕synchronized中什么也不做,线程在再次访问成员变量时,也会从内存中获取。

 

If the built-in Just-In-Time (JIT) compiler isturned off, 

java -Djava.compiler=NONE Volatile 

the code behaves as if value was declared to bevolatile.

 

Tip Use volatile on member variables that can be accessed by two or morethreads unless all the threads access the variables within synchronized blocksof code.

 

因为编译命令行和synchronized(比如你调用println,实际上已经执行了synchronized)的原因,实际情况是,就算没加volatile,也很少出现问题。

 

如何才能模拟出没加volatile就出会出问题的现象?

答:

 

synchronized Method Modifier

简单,略

synchronized Statement Block

简单,略

static synchronized Methods

In addition to the object-level lock that existsfor each instance of a class, there is a class-level lock that all instances ofa particular class share.

 

The class-level lock can be used to controlconcurrent access to static member variables. Even when no variables areinvolved, the synchronized modifier can be used on static methods simply toensure that only one thread is inside the method at a time.

(已验证)

 

Using the Class-Level Lock in a synchronizedStatement

 To lock onthe class-level lock, use the following code 

 synchronized ( ClassName.class ) { 

      //body 

 }

例如

public class StaticTest {

   

    static Object obj = new Object();

   

    static void fn() {

       synchronized (StaticTest.class) { // Ö±½ÓÓþ²Ì¬±äÁ¿objÕÕÑù¿ÉÒÔ

           System.out.println(System.currentTimeMillis() + " enter fun");

           try {

              Thread.sleep(2000);

           } catch (InterruptedException e) {

           }

       }

    }

   

    public static void main(String[] args) {

      

       Thread thread1 = new Thread(new Runnable(){

           public void run() {

              fn();

           }

       });

      

       Thread thread2 = new Thread(new Runnable(){

           public void run() {

              fn();

           }

       });

      

       thread1.start();

       thread2.start();

    }

}

我觉得当静态方法较大,且存在若干需要同步访问的静态成员变量时,用类级锁较合适。如果只需要同步一个静态成员变量,直接用这个变量同步估计都可以(上面的例子已经证明了可以)

 

Synchronization and the Collections API

主要讲了Vector和Hashtable是线程安全的。

The designers of the Collections API wanted toavoid the overhead of synchronization when it wasn’tnecessary. As a result, none of the methods that alter the contents of acollection are synchronized. If a Collection or Map will be accessed bymultiple threads, it should be wrapped by a class that synchronizes all themethods.

There are several static methods in theCollections class that are used to wrap unsynchronized collections withsynchronized methods: 

 publicstatic Collection synchronizedCollection(Collection c) 

 publicstatic List synchronizedList(List l) 

 publicstatic Map synchronizedMap(Map m) 

 publicstatic Set synchronizedSet(Set s) 

 publicstatic SortedMap synchronizedSortedMap(SortedMap sm) 

 publicstatic SortedSet synchronizedSortedSet(SortedSet ss) 

Basically, these methods return new classes thathave synchronized versions of the collections’ methods.To create a List that is multithread-safe and backed by an ArrayList, use thefollowing: 

List list =Collections.synchronizedList(new ArrayList());

Notice that the ArrayList instance was immediatelywrapped and that no direct reference to the unsynchronized ArrayList exists.This is the safest approach.显然同时访问间接和直接引用会出问题,我们不能那样干。

包装了之后,就可线程安全地进行添加删除元素等操作了,但要注意,在进行迭代遍历时,仍然得加锁。

如何Safely Copying the Contents of a List into anArray?

       // First technique (favorite) 

       String[] wordA = (String[])wordList.toArray(new String[0]); //注意toArray的用法

        

       // Second technique 

       String[] wordB; 

       synchronized ( wordList ) { 

           int size =wordList.size(); 

           wordB = new String[size]; 

           wordList.toArray(wordB); 

       }

 

Deadlocks

和C++中类似,避免死锁的办法也类似,略

Speeding Concurrent Access

这儿说同步方法和同步语句块在获取和释放锁上代价是一样的,同步语句块仅仅可能去锁的内容更少一些而已。以前看的哪本上好像说其中一种效率更高些,我也觉得不太可能。

 

Chapter 8: Inter-thread Communication

The Need for Inter-thread Signaling

The Wait/Notify Mechanism

The wait/notify mechanism does not require that avariable be checked by one thread and set by another. However, it is generallya good idea to use this mechanism in conjunction with at least one variable.Doing so helps in avoidingmissed notifications and in detecting early notifications.

Minimal Wait/Notify(也就是不带检查变量)

If the waiting thread is interrupted, it competeswith the other threads to reacquire the object-level lock and throws anInterruptedException from within wait(). If the waiting thread is notified, itcompetes with the other threads to reacquire the object-level lock and thenreturns from wait().注意,不会和调用notify的线程竞争,notify线程已经占有锁,除非退出synchronized块,否则它不可能释放锁。

 

Instead of catching InterruptedException, methodscan simply declare that they throw it to pass the exception further up the callchain: 

    publicvoid someMethod() throws InterruptedException { 

       //... 

      synchronized ( valueLock ) { 

          valueLock.wait(); 

      } 

       //... 

   }

是不是不管什么异常只要在函数定义上抛出了就不用在代码中捕获了?

答:是,相当于直接把函数中发生的异常发送到外层函数中去了,既然已经交给外面捕获去了,当然不用在函数中捕获了。

Typical Wait/Notify(也就是带检查变量)

大多数情况下,一个成员变量被等待线程检查、被通知线程修改。检查和修改发生在同步块内部,这就确保了没有竞争发生。也就是说,这个变量只能用在这个地方作此用途,要是放在没有同步的地方,就会出现竞争了。

用法如下:

        // ³ÉÔ±±äÁ¿

        private boolean value = false

        private Object valueLock= new Object();

        

        // µÈ´ýÏß³Ì

        try

             synchronized ( valueLock) { 

                 while ( value != true ) { 

                    valueLock.wait(); 

                 } 

                 // value is now true 

             } 

         } catch (InterruptedException x ) { 

            System.out.println(“interrupted while waiting”); 

         }

        

         // ֪ͨÏß³Ì

         synchronized ( valueLock) { 

             value = true

            valueLock.notify();  // notifyAll() might be safer... 

         }

To be sure that it was not falsely notified (seethe “Early Notification”discussion later in this chapter), it re-evaluates the while expression.

搞清楚什么是Early Notification,就搞清楚了为什么需要加一个变量?

 

Wait/Notify with synchronized Methods

This action causes threadA to be notified, butthreadA cannot return from wait() until it can get the lock, and threadB isstill holding the lock. 这句话证明了前面我的论断是正确的。

 

Object API Used for Wait/Notify

即notify(),notifyAll(),wait(),wait(long),wait(long,int)

注意,所有的方法都是final native的,通过看源代码看看内部究竟是通过什么实现的?

 

If no threads are currently waiting on the object,notify() has no effect.

Unlike wait(), the invocation of notify() does nottemporarily release the lock.再次和我试验出来的一致。

 

如果知道仅仅有一个wait在等待的话,就用notify,notifyAll比notify耗处理器。

There is no way for the caller to determinewhether a notification or a timeout occurred because no information (void) isreturned from wait(long).真的没有办法了吗?我们经常有这种需求呢!

 

大多数JavaVM实现实际上都不支持纳秒级精度时间,所以the use ofwait(long,int) is currently quite rare.

 

When to Use notifyAll() Instead of notify()

Tip If you’re not sure whether you need to use notify() ornotifyAll(), use notifyAll(). It might be a little wasteful, but it’s safer.

 

Missed Notification

比如主线程启动一个线程去处理一件事情,然后等待处理完的通知。有可能主线程还没进入等待状态,工作线程就已经把事情处理完了,此时再等就是死等了。

To fix MissedNotify, a boolean indicator variableshould be added. The indicator is only accessed and modified insidesynchronized blocks.

如果工作线程先处理完将标识设置为true后,工作线程发现标识已经被修改,就不用再进入等待状态了。(即用这个标识来记录可以进入等待的条件)

Early Notification

比如一个线程往队列中添加元素,另两个线程从队列中删除元素,在加入一个元素后,添加线程调用notifyAll通知删除线程,删除线程从wait返回,开始删除元素。显然,添加了一个元素,却删除了两次,显然不对。只要不是两个wait线程且使用notifyAll,都不会出现问题。书上的解决办法是用循环判断代替if判断。

我觉得这算不上问题,我们一定要精细化处理,比如虽然有两个wait,但我们用notify去通知一个wait就够了。

书上推荐的办法是只要等待条件满足就循环等待,通知方法用notifyAll代替notify。这样也不错,基本上不会出问题(虽不是精细化控制,但是以通用逻辑应万变)。例如

while ( list.isEmpty() ) {  // 用while代替if

     list.wait(); 

}

代替

if ( list.isEmpty() ) { 

     list.wait(); 

}

代替之后,虽然两个wait都返回了,但其中一个执行完删除后,另外一个发现又为空了,那么又满足了等待的条件,又进入了等待,它不会走到删除语句去,就不会存在问题了。当然,就算一个wait或之前MissedNotification情况,也同样没问题。

 

CubbyHole Example

这儿的例子中出现了两个线程互相等待,互相通知的情况,也就是说互等互通完全是正常的用法

 

Using join() to Wait for a Thread to Die

简单,略

 

Streaming Data Between Threads Using Pipes

The java.io package provides many classes forwriting and reading data to and from streams. Most of the time, the data iswritten to or read from a file or network connection. Instead of streaming datato a file, a thread can stream it through a pipe to another thread. The firstthread writes to the pipe, and the second thread reads from the pipe. A pipe isneither a file nor a network connection, but a structure in memory that holdsthe data that is written until it is read. Usually, a pipe has a fixed capacity.When the pipe is filled to this capacity, attempts to write more data willblock waiting until some data is drained (read) from the pipe by anotherthread. Similarly, when a pipe is empty, attempts to read data from the pipewill block waiting until another thread writes some data into it. 

There are four pipe-related classes in the java.iopackage that can be used to stream data between threads: PipedInputStream,PipedOutputStream, PipedReader, and PipedWriter.

 

A pipe made up of a PipedInputStream and aPipedOutputStream has a capacity to hold 1024 bytes.

A pipe made up of a PipedReader and a PipedWriterhas a capacity to hold 1024 characters.

I discovered the size of the pipes (1024 bytes and1024 characters) by examining the source code from Sun Microsystems. The APIdocumentation gives no information or guarantees regarding the internal pipesize. Therefore, you should not depend on 1024 being the universal size.

写个例子试一下是否会出现分包和混包情况?

 

Using ThreadLocal and InheritableThreadLocal

学会使用这两个类,书上这节暂为看懂。

 

Chapter 9: Threads and Swing

Why Isn’t the Swing ToolkitMultithread-Safe?

One of the goals for the developers of Swing wasto make the toolkit as fast as possible. If the components had to bemultithread-safe, there would need to be a lot of synchronized statements andmethods.

 

本章略,平常我们都用SWT,学会SWT的多线程就行了。Swing和SWT的多线程差不多,无非就是把一个函数(Runnbale对象)放到事件队列中,由事件线程取出来调用而已。

 

Chapter 10: Thread Groups

Overview 

In Java, threads can be grouped together andassociated with an instance of ThreadGroup. In this chapter, I’ll showyou how to use some of the methods of ThreadGroup. At the end of the chapter, I’ll showyou how to use the class ThreadViewer to visually display the status of all thethreads running in the Java VM.

 

What Are Thread Groups?

There is only one root thread group, and itcontains all the other threads and groups.

Like Thread, instances of ThreadGroup have namesassociated with them. Unlike Thread, a name must be specified-there is nooption for names to be automatically generated.

如何获取根线程组的名字?

找到当前线程所属的线程组,然后通过线程组类的getParent一直往上找,直到没有父线程组的那个线程组就是根线程组。

 

Using getParent()

就是前面的方法获取根线程组。

 

Finding the Subgroups of a Thread Group

主要是activeGroupCount() 和enumerate的应用。例如

ThreadGroup group = // ... 

int estimatedSize = 2 *group.activeGroupCount(); 

ThreadGroup[] dest = newThreadGroup[estimatedSize]; 

int actualSize =group.enumerate(dest);

 

Using the getThreadGroup() Method of Thread

If a thread is no longer alive, getThreadGroup()returns null instead of a ThreadGroup.

 

Finding All the Threads in a Thread Group

道理同找线程组,例

 ThreadGroup group = // ... 

 int estimatedSize = 2 *group.activeCount(); 

 Thread[] dest = newThread[estimatedSize]; 

 int actualSize = group.enumerate(dest);

 

Understanding Thread Group Security

By default, applications do not have aSecurityManager installed. Applets, on the other hand, might ave one.

A full discussion of security in Java is beyondthe scope of this book. You just need to be aware that a SecurityExceptionmight be thrown from most of the methods of ThreadGroup. If you are writing anapplication, you can usually safely ignore these checkAccess() issues.

 

Using interrupt()

The interrupt() method of ThreadGroup can be usedto signal an interrupt to all the threads in the group and subgroups. Thismethod can be useful if several threads have been spawned to handle a task, andit’s time tosignal all of them to shut down.

 

Deprecated Methods: stop(), suspend(), andresume()

略,道理同Interrupt()

 

Class ThreadViewer

The class ThreadViewer (see Listing 10.1)graphically displays all of the threads currently running in the Java VM. Itautomatically refreshes itself every 5 seconds to keep current. ThreadViewercan be a handy tool to have around during the development and debugging ofmultithreaded applications.

略,因为我们自己也能容易实现出来。

 

Chapter 11: Self-Running Objects

Sometimes in modeling a system, it becomesapparent that if some of the objects were active, the model would besimplified.(就是把线程隐藏在一个对象中,随着对象的产生而产生,消灭而消灭)

Summary 

In this chapter, I showed you a technique forcreating active classes that are self-running. Self-running classesautomatically create an internal thread that runs inside the object. Thisthread is usually started in the constructor, but can be started later ifnecessary. Users of self-running objects do not need to concern themselves withthe details of creating and starting a thread for an object-in fact, they don’t evenneed to know that a thread is running inside it at all. Additionally, becausethe users of a self-running object do not have a reference to the internalthread, they cannot erroneously use deprecated methods to suspend or stop thethread at a bad time. 

Of the two self-running designs presented, Irecommend that you use the one that hides run() within an anonymous, innerclass. This design has the advantage of preventing a user from mistakenlyinvoking run() directly, or from mistakenly creating a new thread that invokesrun().

由于简单,其他细节就不多说了,在工作中用一用就清楚了。

书上结束线程是提供了一个方法供外部调用,有没有办法像C++那样在析构函数中结束线程,这样就不用显式提供结束线程的接口函数了?

答:


工作中遇到的疑问

1.在做任务加载时,当响应消息到达的时候,请求线程还没进入等待状态,也就是先notify,后wait,这样wait能正常得到通知吗?

答:不行。证明如下

public class HelloWorld {

    public static void main(String[] arg) {

 

      

       final String str = new String("abc");

      

       Thread thread = new Thread(new Runnable(){

 

           @Override

           public void run() {

              System.out.println(System.currentTimeMillis() + "enter run" );

              synchronized (str) {

                  System.out.println(System.currentTimeMillis() + "enter run-synchronized" );

                  str.notify();

              }

              System.out.println(System.currentTimeMillis() + "leave run");

           }

   

          

       });

      

       thread.start();

      

       try {

           Thread.sleep(2000);

       } catch(InterruptedException e) {

           ;

       }

      

       try {

           synchronized(str){

              System.out.println(System.currentTimeMillis() + "enter wait");

              str.wait(5000); // ÔçÔçµØnotify¾Í·¢ÉúÁË£¬虽然已经获得str,但str的唤醒标识没有被设置,所以Ò»Ö±»áµÈµ½³¬Ê±(即调用wait时会先将str的唤醒标识设置为假然后等待唤醒标识被设置为真)

           }

           System.out.println(System.currentTimeMillis() + "leave wait");

          

       } catch(InterruptedException e) {

           ;

       }

    }

}

 

2.wait和notify之间究竟是一种什么样的同步过程?调用notify后,两个线程能同时执行吗?

答:同步过程参考例子,搞懂同步过程后,可以得出:调用notify后,两个线程显然不能能同时执行,只有一个str锁,怎么可能两个线程同时运行呢,呵呵。

public class HelloWorld {

    public static void main(String[] arg) {

 

      

       final String str = new String("abc");

      

       Thread thread = new Thread(new Runnable(){

 

           @Override

           public void run() {

              try {

                  Thread.sleep(1000);

              } catch (InterruptedException e) {

                  ;

              }

             

              synchronized (str) { // µ±»ñµÃstrµÄʱºò¾Í½øÈë

                  System.out.println(System.currentTimeMillis() + "enter run-synchronized" );

                  // ¸østrÉèÖÃÒ»¸ö֪ͨ(»½ÐÑ)±êʶ£¬ÓÉÓÚstrÒѾ­±»µ±Ç°Ïß³ÌÕ¼×Å£¬ÁíÒ»¸öÏ߳̿϶¨±»×èÈû

                  str.notify();

                 

                  try {

                     Thread.sleep(1000);

                  } catch (InterruptedException e) {

                     ;

                  }

                  //Í˳ösynchronized¶Îʱ£¬×Ô¶¯ÊÍ·Åstr

              }

              System.out.println(System.currentTimeMillis() + "leave run");

           }

   

          

       });

       

       thread.start();

      

       try {

           synchronized(str){ // µ±»ñµÃstrµÄʱºò¾Í½øÈë

              System.out.println(System.currentTimeMillis() + "enter wait");

             

              try {

                  Thread.sleep(5000);

              } catch (InterruptedException e) {

                  ;

              }

             

              str.wait(5000); // ½øÈëwait״̬µÄʱºò£¬Ï൱ÓÚÊÍ·ÅstrËø

             

              //µ±ÁíÒ»¸öÏß³ÌÊÍ·Åstrºó£¬ÒòΪstrÒѾ­±»ÉèÖû½Ðѱêʶ£¬wait·µ»Ø£¬±¾Ï̼߳ÌÐøÖ´ÐÐ

              try {

                  Thread.sleep(2000);

              } catch (InterruptedException e) {

                  ;

              }

           }

           System.out.println(System.currentTimeMillis() + "leave wait");

          

       } catch (InterruptedException e) {

           ;

       }

    }

}

运行结果:

1257872441187enterwait       因为工作线程sleep了1秒,所以主线程首先获得str锁进入synchronized

1257872446187enterrun-synchronized  一直等到主线程5秒后调用wait释放了str锁,工作线程才进入synchronized

1257872447187leave run  工作线程给锁设置唤醒标识后,睡眠1秒后释放str锁

1257872449187leave wait 主线程从wait返回,睡眠2秒后释放锁,因为要等到工作线程释放锁之后wait返回,而不是notify返回,所以主线程从被notify到退出段花了3秒时间,而不是以前想象的2秒。所以嘛,java的这个同步和Win32中的事件内核对象机制差别还挺大的。

 

3.为什么wait和notify外面必须加synchronized(str)?

答:从上面的理解可以知道,wait和notify都是对当前线程占有的锁进行操作,比如wait释放占有的锁,notify会对锁进行修改,你都没占有锁你释放什么呢,你都没拥有锁,你能修改它么!

 

4.有没有可能两个线程都进入了synchronized(str)段?

答:从上面看,是完全可能的。因为wait会释放synchronized(str)到的锁,此时其他线程就可以进入了。

 

5.线程先结束后join可以吗?

答:可以(已验证)

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值