线程基础:线程(2)——JAVA中的基本线程操作(上)

文章注明来源:http://blog.csdn.net/yinwenjie,主要供自己学习转载用

1、JAVA中线程的状态

1-1、#对象锁

  • Java中每一个对象都有一把‘锁’,这个‘锁’可以是开放状态;也可以由某一个线程(或者多个线程)持有‘钥匙’;一旦在系统中某个对象的‘锁’存在至少一把‘钥匙’,那么任何线程操作这个对象,都必须验证是否有‘钥匙’,如果没有则会报IllegalMonitorStateException异常。
  • 可是‘锁芯’(对象独占权)只有一个,那么可以打开这把锁的多个‘钥匙’同一时间内只能有一把‘钥匙’进行操作;其他持有‘钥匙’的线程(或者没有持有钥匙的线程)都要进入等待状态;直到某把‘钥匙’从‘锁眼’中退出,操作系统会决定哪一把‘钥匙’重新插入‘锁芯’(操作系统线程切换)。
  • 某一个线程在执行过程中,可以随时为任何一个对象‘加锁’;也可以随时归还这个对象锁的‘钥匙’。如果归还了这个对象锁的‘钥匙’,那么这个线程(在对象加了锁的状态下)肯定就不再具有这个对象的操作权了。
  • 需要注意:某一个线程拥有一个对象的‘锁’的‘钥匙’,并不代表这个线程的‘钥匙’是插入了‘锁芯’的(有这个对象的控制权);但是,有权抢占‘锁芯’控制权的线程,必定拥有这个对象的‘钥匙’。
  • 线程中,‘锁芯’代表对象的独占控制权;需要对某个对象的锁状态进行检测的关键字为‘synchronized’(临界区)。能将钥匙拔出锁芯,但是又不归还锁的调用方法(释放对象独占状态,并能在后续参与对象独占竞争的方法),只有wait和wait(time)。归还钥匙的方式包括:线程中针对某个对象的临界区正常完成、有异常抛出到临界区外(临界区异常完成)

请一定注意wait(time)的用法。很多初学者(包括我本人在很长一段时间)的理解都是:‘等待一段时间time,然后该线程激活继续工作’;但是如果您按照上文的描述,就会发现这样的理解是有问题的。实际上time更准确的含义应该是:到时检查。而整个wait(time)更准确的理解应该是:释放这个线程独占的X对象的锁芯(独占权),以便其它可以抢占‘锁芯’(独占权)的线程能够进行抢占,但是本线程继续持有X对象的锁的钥匙,等待time的时间后,重新参与‘锁芯’抢占(不一定能够抢占得到)。

1-2、对象锁的工作过程

下图表示了线程中,对象锁的工作过程:

我们以一个实际代码例子来看看所描述的锁的工作过程:

 1 package test.thread.lock;
 2
 3 import org.apache.commons.logging.Log;
 4 import org.apache.commons.logging.LogFactory;
 5
 6 public class ThreadLock {
 7
 8     /**
 9      * 拿来加锁的对象
10      */
11     private static final Object WAIT_OBJECT = new Object();
12
13     /**
14      * 日志,如果您没有log4j,请使用System.out
15      */
16     private static final Log LOGGER = LogFactory.getLog(ThreadLock.class);
17
18     /**
19      * 偷懒,我把异常全部抛出了。正式系统不要这么搞哦!!
20      * @param args
21      * @throws Exception
22      */
23     public static void main(String[] args) throws Exception {
24         Thread threadA = new Thread(new Runnable() {
25             @Override
26             public void run() {
27                 // 检查‘对象锁‘状态。
28                 synchronized (ThreadLock.WAIT_OBJECT) {
29                     ThreadLock.LOGGER.info("做了一些事情。。。。");
30                 }
31             }
32         });
33
34         Thread threadB = new Thread(new Runnable() {
35             @Override
36             public void run() {
37                 // 检查‘对象锁‘状态。
38                 synchronized (ThreadLock.WAIT_OBJECT) {
39                     ThreadLock.LOGGER.info("做了另一些事情。。。。");
40                 }
41             }
42         });
43         threadA.start();
44         threadB.start();
45     }
46 }

代码很简单,应该不需要做过多说明吧:创建了两个线程threadA和threadB。两个线程的执行方法中都需要对ThreadLock.WAIT_OBJECT这个Object进行对象锁状态检查。下面我们通过eclipse的调试状态看看线程的执行过程:

  • threadA和threadB开始工作了。请注意,这是两个线程都没有持有任何对象锁的钥匙:
  • ThreadA开始进行ThreadLock.WAIT_OBJECT对象锁检查,发现没有任何线程占有这个对象锁的锁芯。于是JVM为这个线程创建一把对象锁的钥匙,并让这个线程的钥匙占据锁芯(独占这个对象的操作权):
  • 这时ThreadB也开始进行ThreadLock.WAIT_OBJECT对象锁检查,但是发现ThreadLock.WAIT_OBJECT对象锁的锁芯已经被某个线程占据(ThreadA正在独占),于是停留在检查位等待:
  • ThreadA继续运行,直到synchronized关键字标示的临界区全部执行完毕,于是释放锁芯,归还锁。ThreadB发现ThreadLock.WAIT_OBJECT不再处于独占状态(锁芯没有任何钥匙插在哪里),于是解除等待状态,获得ThreadLock.WAIT_OBJECT的对象锁钥匙,插入锁芯,继续执行:

1-3、synchronized可标注的位置

在JAVA中synchronized关键字可以加载很多位置。您可以在一个方法定义上加synchronized关键字、也可以在方法体中加synchronized关键字、还可以在static块中加synchronized关键字。如下的代码都是正确的:

 1 // 代码片段1
 2 static {
 3     synchronized(ThreadLock.class) {
 4
 5     }
 6 }
 7
 8 // 代码片段2
 9 public synchronized void someMethod() {
10
11 }
12
13 // 代码片段3
14 public synchronized static void someMethod() {
15
16 }
17
18 // 代码片段4
19 public static void someMethod() {
20     synchronized (ThreadLock.class) {
21
22     }
23 }
24
25 // 代码片段5
26 public void someMethod() {
27     synchronized (ThreadLock.class) {
28
29     }
30 }

但是不同位置的synchronized的关键字,代表的含义是不一样的。synchronized(){}这个写法,开发人员可以指定需要检查的对象锁。但是当synchronized加载在方法上时,有的读者就感觉有点混淆了。这里详细说明一下:

  • synchronized关键字加载在非静态方法上时: 其代表的含义和synchronized(this){}的意义相同。即对所拥有这个方法的对象进行对象锁状态检查。
  • synchronized关键字加载在静态方法上时: 其代表的含义和synchronized(Class.class)的意义相类似。即对所拥有这个方法的类的对象进行对象锁状态检查(类本身也是一个对象哦 ^_^)。
时间: 2024-10-21 07:37:59

线程基础:线程(2)——JAVA中的基本线程操作(上)的相关文章

线程基础:线程(3)——JAVA中的基本线程操作(中)

(接上文<线程基础:线程(2)--JAVA中的基本线程操作(上)>) 1-4.注意synchronized关键字的使用 在前面的文章中我们主要讲解的是线程中"对象锁"的工作原理和操作方式.在讲解synchronized关键字的时候,我们还提到了synchronized关键字可以标注的位置.大家经常看到相当部分的网贴,在它们的代码示例中将synchronized关键字加载到代码的方法体上,然后告诉读者:这个操作是线程安全的.代码可能如下: /** * 这个类的class对象进

线程基础:JDK1.5+(8)——线程新特性(上)

1.概要 如果您阅读JAVA的源代码,出现最多的代码作者包括:Doug Lea.Mark Reinhold.Josh Bloch.Arthur van Hoff.Neal Gafter.Pavani Diwanji等等.其中java.util.concurrent包中出现的基本都是Doug Lea的名字.Doug Lea,是对Java影响力最大的个人,直接贡献的设计包括java的Collections和util.concurrent. JDK1.5中一个重要特性就是util.concurrent

Java中事件分发线程(EDT)与SwingUtilities.invokeLater相关总结

前言:这篇文章严格来说不算原创,算是我对这方面知识的一点小结,素材来至其他网友.当然我在我写的C段查询工具也用到了这方面的东西,不过由于代码太多不方便用作事例,因此用了他人的素材总结一下,望理解O(∩_∩)O~ 一 Swing线程基础 一个Swing程序中一般有下面三种类型的线程:    * 初始化线程(Initial Thread)    * UI事件调度线程(EDT)    * 任务线程(Worker Thread)每个程序必须有一个main方法,这是程序的入口.该方法运行在初始化或启动线程

线程基础:JDK1.5+(9)——线程新特性(中)

(接上文<线程基础:JDK1.5+(8)--线程新特性(上)>) 3.工作在多线程环境下的"计数器": 从这个小节開始,我们将以一个"赛跑"的样例.解说JDK1.5环境下一些线程控制工具(包含Semaphore.CountDownLatch和java.util.concurrent.atomic子包),而且复习这个专题讲到的知识点:同步快.锁.线程池.BlockingQueue.Callable等. 3-1. 赛跑比赛的需求 如今您不仅能够通过我们已经介

Java中的守护线程

Java中的守护线程 Java中的守护线程与UNIX中的守护线程概念不同,UNIX中的守护线程相当于一项服务,一直运行在后台,而Java中的守护线程是这样定义的: A daemon thread is a thread, that does not prevent the JVM from exiting when the program finishes but the thread is still running. 也就是说,当程序中只有守护线程时,JVM就会自动退出,典型的守护线程就是垃

关于Java中进程和线程的详解

一.进程:是程序的一次动态执行,它对应着从代码加载,执行至执行完毕的一个完整的过程,是一个动态的实体,它有自己的生命 周期.它因创建而产生,因调度而运行,因等待资源或事件而被处于等待状态,因完成任务而被撤消.反映了一个程序在 一定的数据 集上运行的全部动态过程.通过进程控制块(PCB)唯一的标识某个进程.同时进程占据着相应的资源(例如包 括cpu的使用 ,轮转时间以及一些其它设备的权限).是系统进行资源分配和调度的一个独立单位. 程序和进程之间的主要区别在于: 状态         是否具有资源

JAVA学习篇--ThreadLocal,Java中特殊的线程绑定机制

在DRP项目中,我们使用了ThreadLocal来创建Connection连接,避免了一直以参数的形式将Connection向下传递(传递connection的目的是由于jdbc事务要求确保使用同一个connection连接).那么ThreadLocal是如果做到的呢?它和同步锁的不同在哪里? 是什么: 对于ThreadLocal看英文单词我们很容易理解为一个线程的本地实现,但是它并不是一个Thread,而是threadlocalvariable(线程局部变量).也许把它命名为ThreadLoc

线程基础:JDK1.5+(10)——线程新特性(下)

(接上文<线程基础:JDK1.5+(9)--线程新特性(中)>) 3-4.CountDownLatch:同步器 上文中我们主要讲解了JDK1.5+中提供的一个重要工具:Semaphore信号量,并且用这个工具第一次实现了"100米赛跑"的需求.在第一次的实现中,我们还运用了"线程专栏"中已介绍的多个知识点,包括锁.线程池.队列.Callable接口等. 但实际上第一次实现的"100米赛跑"的需求,离我们真正的需求还有一定的距离:在我们

java中 mongodb的各种操作

一. 常用查询: 1. 查询一条数据:(多用于保存时判断db中是否已有当前数据,这里 is  精确匹配,模糊匹配 使用 regex...) public PageUrl getByUrl(String url) { return findOne(new Query(Criteria.where("url").is(url)),PageUrl.class); } 2. 查询多条数据:linkUrl.id 属于分级查询 public List<PageUrl> getPageU