Summary of basic knowledge (2)

2017.10 work summary

10.1 
(1) The difference between memory leak and memory overflow
Memory overflow out of memory means that when the program applies for memory, there is not enough memory space for it to use, and out of memory occurs; for example, if an integer is applied for, but the number that can be stored by long is stored for it, that is memory overflow .
Memory leak memory leak means that the program cannot release the requested memory space after applying for memory 
Memory leak will eventually lead to out of memory!
(2) Thread sharing within vm
Most JVMs divide the memory area into  Method Area (Non-Heap) (method area)  , Heap (heap)  ,  Program Counter Register (program counter)  ,    VM Stack (virtual machine stack, also translated into JAVA method stack), Native Method Stack   (  local method stack  ), where Method Area  and  Heap  are shared by threads   , and VM Stack, Native Method Stack and Program Counter Register   are shared by non-threads.

(3) JVM determines when the object is recycled
In mainstream implementations in mainstream commercial programming languages ​​(Java, C#, and even the aforementioned ancient Lisp),
It is called by Reachability Analysis to determine whether the object is alive or not. The basic idea of ​​this algorithm
The path is through a series of objects called "GC Roots" as the starting point, starting from these nodes and searching down, searching for all
The path traversed is called the Reference Chain, when an object does not have any reference chain connected to the GC Roots
(In the words of graph theory, the object is unreachable from the GC Roots), then it is proved that the object is unusable.

(4) Thread properties
  • GroupName , each thread will be in a thread group by default, we can also create a thread group explicitly, and a thread group can also contain sub-thread groups, so that threads and thread groups form a tree structure.
  • Name , each thread will have a name, if not explicitly specified, then the name rule is "Thread-xxx".
  • Priority , each thread will have its own priority, and the JVM handles the priority in a "preemptive" way. When the JVM finds a thread with a high priority, it runs the thread immediately; for multiple threads with equal priorities, the JVM polls them. Java's thread priority ranges from 1 to 10, and the default is 5. The Thread class defines two constants: MIN_PRIORITY and MAX_PRIORITY to represent the highest and lowest priorities.
  • Each thread has a priority attribute when it executes. Threads with higher priorities can get more execution opportunities, while threads with lower priorities get fewer execution opportunities. Similar to thread sleep, the priority of the thread still cannot guarantee the execution order of the thread. However, a thread with a high priority has a higher probability of obtaining CPU resources, and a thread with a low priority has no chance to execute .
  • isDaemon , this property is used to control the relationship between parent and child threads. If it is set to true, when the parent thread ends, all child threads under it also end. On the contrary, the life cycle of the child thread is not affected by the parent thread.

(5) Daemon thread
There are two types of threads in Java: User Thread and Daemon Thread.
The so-called daemon thread refers to a thread that provides a general service in the background when the program is running. For example, the garbage collection thread is a very competent guardian, and this thread is not an indispensable part of the program. Therefore, when all non-daemon threads end, the program terminates, killing all daemon threads in the process. Conversely , the program will not terminate as long as any non-daemon threads are still running .
There is almost no difference between user threads and daemon threads. The only difference is the departure of the virtual machine: if the user threads have all exited from running, only the daemon threads exist, and the virtual machine will exit. Because there is no guardian, the daemon thread has no work to do, and there is no need to continue running the program.
Converting a thread to a daemon thread can be done by calling the setDaemon(true) method of the Thread object.

(6) Creation of threads
◆Need to derive a new thread class from Java.lang.Thread class and overload its run() method; 
◆ Implement the Runnalbe interface and overload the run() method in the Runnalbe interface.
Why does Java provide two methods to create threads? What are the differences between them? In comparison, which method is better?
In Java, classes only support single inheritance, that is, when a new class is defined, it can only extend an external class. In this way, if a custom thread class is created, it is implemented by extending the method of the Thread class , then this custom class can no longer extend other classes, and it cannot implement more complex functions. Therefore, if a custom class must extend other classes, it can use the method that implements the Runnable interface to define the class as a thread class, thus avoiding the limitations brought by Java single inheritance.
还有一点最重要的就是使用实现Runnable接口的方式创建的线程可以处理同一资源,从而实现资源的共享.(因为可以同时通过thread类开几条线程处理该资源)

(7)   通过实现Runnable接口来实现线程间的资源共享
可见, 如果现实问题中要求必须创建多个线程来执行同一任务,而且这多个线程之间还将共享同一个资源,那么就可以使用实现Runnable接口的方式来创建多线程程序 。而这一功能通过扩展Thread类是无法实现的, 想想看,为什么?

(8)线程切换
线程六种状态
  • 创建:已经有Thread实例了, 但是CPU还没有为其分配资源和时间片。
  • 就绪:线程已经获得了运行所需的所有资源,只等CPU进行时间调度。
  • 运行:线程位于当前CPU时间片中,正在执行相关逻辑。
  • 休眠:一般是调用Thread.sleep后的状态,这时线程依然持有运行所需的各种资源,但是不会被CPU调度。
  • 挂起:一般是调用Thread.suspend后的状态,和休眠类似,CPU不会调度该线程,不同的是,这种状态下,线程会释放所有资源。
  • 死亡:线程运行结束或者调用了Thread.stop方法。

线程状态切换方法
  • Thread()或者Thread(Runnable):构造线程。
  • Thread.start:启动线程。
  • Thread.sleep:将线程切换至休眠状态。(sleep)是静态方法,由类直接调用。
  • Thread.interrupt:中断线程的执行。
  • Thread.join:等待某线程结束。
  • Thread.yield:剥夺线程在CPU上的执行时间片,等待下一次调度。
  • Object.wait:将Object上所有线程锁定,直到notify方法才继续运行。
  • Object.notify:随机唤醒Object上的1个线程。
  • Object.notifyAll:唤醒Object上的所有线程。

(9) thread的构造方法
eg  : public Thread(Runnable target, String name);
Runnable target
实现了 Runnable 接口的类的实例。要注意的是 Thread 类也实现了 Runnable 接口,因此,从 Thread 类继承的类的实例也可以作为 target 传入这个构造方法。
String name
线程的名子。这个名子可以在建立 Thread 实例后通过 Thread 类的 setName 方法设置。如果不设置线程的名子,线程就使用默认的线程名: Thread-N N 是线程建立的顺序,是一个不重复的正整数。

10.9
(1)synchronized关键字 (本质上是一种悲观锁)
多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题。
同步机制可以使用 synchronized关键字 实现。
当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
当synchronized方法执行完或发生异常时,会自动释放锁。

(2) 一个对象存在多个synchronized关键字时

如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。  
     结论: 当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
  Java中的每个对象都有一个锁(lock),或者叫做监视器(monitor),当一个线程访问某个对象的synchronized方法时, 将该对象上锁 其他任何线程都无法再去访问该对象的synchronized方法了(这里是指所有的同步方法,而不仅仅是同一个方法) ,直到之前的那个线程执行方法完毕后(或者是抛出了异常),才将该对象的锁释放掉,其他线程才有可能再去访问该对象的synchronized方法。
  注意这时候是 给对象上锁 ,如果是不同的对象,则各个对象之间没有限制关系。

(3)静态类中的synchronized关键字
    如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的类所对应的Class对象。Java中,无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,它们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始。

(4)synchronized块
synchronized方法 是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;
   synchronized块 则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的其他代码是可以被多个线程同时访问到的。
   synchronized方法实际上等同于用一个synchronized块包住方法中的所有语句,然后在synchronized块的括号中传入this关键字。 当然,如果是静态方法,需要锁定的则是class对象。
10.15 

(1)线程的等待与唤醒

wait(), notify(), notifyAll()等方法介绍

  在Object.java中,定义了wait(), notify()和notifyAll()等接口。wait()的作用是让当前线程进入等待状态,同时,wait()也会让当前线程释放它所持有的锁。而notify()和notifyAll()的作用,则是唤醒当前对象上的等待线程;notify()是唤醒单个线程,而notifyAll()是唤醒所有的线程。
Object类中关于等待/唤醒的API详细信息如下:
notify()         -- 唤醒在此对象监视器上等待的单个线程。
notifyAll()    -- 唤醒在此对象监视器上等待的所有线程。
wait()                                          -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法”,当前线程被唤醒(进入“就绪状态”)。
wait(long timeout)                     -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量”,当前线程被唤醒(进入“就绪状态”)。
wait(long timeout, int nanos)   -- 让当前线程处于“等待(阻塞)状态”,“直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量”,当前线程被唤醒(进入“就绪状态”)。
10.17

第5部分 为什么notify(), wait()等函数定义在Object中,而不是Thread中

Object中的wait(), notify()等函数,和synchronized一样,会对“对象的同步锁”进行操作。
wait()会使“当前线程”等待,因为线程进入等待状态,所以线程应该释放它锁持有的“同步锁”,否则其它线程获取不到该“同步锁”而无法运行!
OK,线程调用wait()之后,会释放它锁持有的“同步锁”;而且,根据前面的介绍,我们知道:等待线程可以被notify()或notifyAll()唤醒。现在,请思考一个问题:notify()是依据什么唤醒等待线程的?或者说,wait()等待线程和notify()之间是通过什么关联起来的?答案是: 依据“对象的同步锁”
负责唤醒等待线程的那个线程(我们称为“ 唤醒线程 ”),它只有在获取“该对象的同步锁”( 这里的同步锁必须和等待线程的同步锁是同一个 ),并且调用notify()或notifyAll()方法之后,才能唤醒等待线程。虽然,等待线程被唤醒;但是,它不能立刻执行,因为唤醒线程还持有“该对象的同步锁”。必须等到唤醒线程释放了“对象的同步锁”之后,等待线程才能获取到“对象的同步锁”进而继续运行。
总之,notify(), wait()依赖于“同步锁”,而“同步锁”是对象锁持有,并且每个对象有且仅有一个!这就是为什么notify(), wait()等函数定义在Object类,而不是Thread类中的原因。








Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324754172&siteId=291194637