Java 多线程编程总结

xiaoxiao2021-02-27  315

Java 多线程编程总结内容

一,synchronized 关键字的使用

 带有 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 所在的线程就可以继续执行。 

转载请注明原文地址: https://www.6miu.com/read-4195.html

最新回复(0)