多线程状态、内存模型、synchronized、volatile
Java线程的生命周期和状态
当创建一个Thread子类或实现Runnable接口类的实例时,线程进入【初始】状态;
调用实例的start方法后,线程进入【可执行】状态;
系统会在某一时刻自动调度处于【可执行】状态的线程,被调度的线程会调用run方法,进入【执行中】状态;
线程执行完run方法后,进入【结束】状态;
处于【结束】状态的线程,在某一时刻,会被JVM垃圾回收;
处于【执行中】状态的线程,若调用了Thread.yield方法,会回到【可执行】状态,等待再次被调度;
处于【执行中】状态的线程,若调用了wait方法,会进入wait set并一直等待,直到被其它线程通过notify、notifyAll、interrupt方法唤醒;
处于【执行中】状态的线程,若调用了Thread.sleep方法,会进入【Sleep】状态,无法继续向下执行。当sleep时间结束或被interrupt时,会回到【可执行状态】;
处于【执行中】状态的线程,若遇到阻塞I/O操作,也会停止等待I/O完成,然后回到【可执行状态】;
Java的内存模型
Java内存模型(memory model)分为主存储器(main memory)和工作存储器(working memory)两种。
主存储器(main memory)
类的实例所存在的区域,main memory为所有的线程所共享。
工作存储器(working memory)
每个线程各自独立所拥有的作业区,在working memory中,存有main memory中的部分拷贝,称之为工作拷贝(working copy)。
工作拷贝的内容便会复制到主存储器(store->write),至于何时进行复制,由JVM决定。
Java语言规范定义了线程的六种原子操作
read
负责从主存储器(main memory)拷贝到工作存储器(working memory)
write
与上述相反,负责从工作存储器(working memory)拷贝到主存储器(main memory)
use
表示线程引用工作存储器(working memory)的值
assign
表示线程将值指定给工作存储器(working memory)
lock
表示线程取得锁定
unlock
表示线程解除锁定
线程欲进入synchronized的时候
强制写入主存储器(main memory)
如果该线程的工作存储器(working memory)上有未映像到主存储器的拷贝,则这些内容会强制写入主存储器(store->write),则这些计算结果就会对其它线程可见(visible)。
工作存储器(working memory)的释放
工作存储器上的工作拷贝会被全部丢弃。之后,欲引用主存储器上的值的线程,必定会从主存储器将值拷贝到工作拷贝(read->load)。
线程欲退出synchronized的时候
强制写入主存储器(main memory)
如果该线程的工作存储器(working memory)上有未映像到主存储器的拷贝,则这些内容会强制写入主存储器(store->write),则这些计算结果就会对其它线程可见(visible)。
线程欲退出synchronized时,不会执行工作存储器(working memory)的释放 操作。
volatile和static是否可以同时用来修饰一个变量或函数?
static关键字表示该变量或函数具有静态存储期,即该变量或函数在程序执行期间只会被初始化一次,且该变量或函数的作用域仅限于当前文件或当前函数内。
volatile关键字则表示该变量或对象可能会被意外地改变,因此编译器不应该对它进行优化,必须每次从内存中读取该变量的值,而不是使用缓存的值。
同时使用volatile和static可以实现一个静态的、可变的变量或函数。
例如,可以将一个静态的全局变量声明为volatile,这样在多线程程序中,每个线程都可以读取该变量的最新值,而不是使用缓存的值。同样,也可以将一个静态的函数声明为volatile,这样可以确保每次调用该函数时,它都会被执行,并返回最新的结果。
需要注意的是,使用volatile关键字会影响编译器对变量的优化,可能会导致一些性能上的损失,因此应该谨慎使用。