Java 多线程编程总结内容
带有 synchronized 关键字的方法代表这个方法加锁。恰当而又灵活地运用 synchronized 关键字,是多线程编程的必修课。1、synchronized关键字的作用域有二种:
1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法; 2)是某个类的范围,synchronized static aStaticMethod(){}防止多个线程同时访问这个类中的synchronized static 方法。它可以对类的所有对象实例起作用。 2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象。 synchronized(this)用于定义一个临界区(即是:其后面大括弧所包含的部分),以保证在多线程下只能有一个线程可以进入this对象的临界区。 也就是说synchronized(对象或者变量){},表示在{}内的这段代码中,对(对象或者变量)中的对象或者变量进行同步处理,也就是说当访问这段代码时,一个线程对对象或者变量的访问完成后才能够交给另外一个线程,即有了同步锁。使用 synchronized(对象或者变量),就是为了防止对象或者变量被同时访问,避免多个线程修改和读取对象或者变量同时出现的时候,使用这个对象或者变量的地方出现错误的判断,如 while(对象或者变量)。 3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法。
二、wait() 和 notify()的使用
主程序 ThreadA 的源代码如下:
[java] view plain copy print ? class ThreadA { public static void main(String[] args) { ThreadB b = new ThreadB(); b.start(); System.out.println("b is start...."); synchronized (b)// 括号里的b是什么意思,起什么作用? { try { System.out .println("Waiting for b to complete..."); b.wait();// 这一句是什么意思,究竟让谁wait? System.out .println("Completed.Now back to main thread"); } catch (InterruptedException e) { } } System.out.println("Total is :" + b.total); } } ThreadB 的源代码如下: [java] view plain copy print ? class ThreadB extends Thread { int total; public void run() { synchronized (this) { System.out.println("ThreadB is running.."); for (int i = 0; i < 100; i++) { total += i; System.out.println("total is " + total); } notify(); } } }要分析这个程序,首先要理解 notify() 和 wait(),为什么在前几天纪录线程的时候没有纪录这两个方法呢,因为这两个方法本来就不属于 Thread 类,而是属于最底层的 object 基础类的,也就是说不光是 Thread,每个对象都有 notify 和wait 的功能,为什么?因为他们是用来操纵锁的,而每个对象都有锁,锁是每个对象的基础,既然锁是基础的,那么操纵锁的方法当然也是最基础了。 再往下看之前呢,首先最好复习一下 Think in Java 的 14.3.1 中第 3 部分内容:等待和通知,也就是 wait() 和 notify 了。 按照 Think in Java 中的解释,wait() 答应我们将线程置入“睡眠”状态,同时又“积极”地等待条件发生改变。而且只有在一个 notify() 或 notifyAll() 发生变化的时候,线程才会被唤醒,并检查条件是否有变。 我们来解释一下这句话。 “ wait() 答应我们将线程置入‘睡眠‘状态“,也就是说,wait 也是让当前线程阻塞的,这一点和 sleep 或者 suspend 是相同的。那和 sleep,suspend 有什么区别呢? 区别在于“(wait) 同时又‘积极‘地等待条件发生改变“,这一点很要害,sleep 和 suspend 无法做到。因为我们有时候需要通过同步(synchronized)的帮助来防止线程之间的冲突,而一旦使用同步,就要锁定对象,也就是获取对象锁,其它要使用该对象锁的线程都只能排队等着, 等到同步方法或者同步块里的程序全部运行完才有机会。在同步方法和同步块中,无论 sleep() 还是 suspend() 都不可能自己被调用的时候解除锁定,他们都霸占着正在使用的对象锁不放。 而 wait 却可以,它可以让同步方法或者同步块暂时放弃对象锁,而将它暂时让给其它需要对象锁的人(这里应该是程序块,或线程)用,这意味着可在执行 wait() 期间调用线程对象中的其他同步方法!在其它情况下( sleep 啊,suspend 啊),这是不可能的。 但是注重我前面说的,只是暂时放弃对象锁,暂时给其它线程使用,我 wait 所在的线程还是要把这个对象锁收回来的呀。wait 什么?就是 wait 别人用完了还给我啊! 好,那怎么把对象锁收回来呢? 第一种方法,限定借出去的时间。在 wait() 中设置参数,比如 wait(1000),以毫秒为单位,就表明我只借出去 1 秒钟。一秒钟之后,我自动收回。 第二种方法,让借出去的人通知我,他用完了,要还给我了。这时,我马上就收回来。哎,假如我设了 1 小时之后收回,别人只用了半小时就完了,那怎么办呢?当然用完了就收回了,还管我设的是多长时间啊。 那么别人怎么通知我呢?相信大家都可以想到了:notify(),这就是最后一句话“而且只有在一个 notify() 或 notifyAll() 发生变化的时候,线程才会被唤醒“的意思了。 因此,我们可将一个 wait() 和 notify() 置入任何同步方法或同步块内部,无论在那个类里是否预备进行涉及线程的处理。而且实际上,我们也只能在同步方法或者同步块里面调用 wait() 和 notify()。 这个时候我们来解释上面的程序,简直是易如反掌了。 synchronized(b){...};的意思是定义一个同步块,使用 b 作为资源锁。b.wait();的意思是临时释放锁,并阻塞当前线程,好让其他使用同一把锁的线程有机会执行,在这里要用同一把锁的就是 b 线程本身。这个线程在执行到一定地方后用 notify() 通知 wait 的线程锁已经用完,待 notify() 所在的同步块运行完之后,wait 所在的线程就可以继续执行。