Java面向对象之多线程运行机制

Java较为实用的多线程设计与应用

多线程机制讨论


电梯调度算法

请求分配顺序html

自动化测试方法

  • 生成随机数据
  • 验证输出数据正确性
  • 打包本身的代码文件
  • 运行评测机

线程安全与锁:https://www.cnblogs.com/zhaojinhui/p/5526988.html

  • lock
  • condition
  • reentrantlock
  • Semaohrone信号量

代码耦合性和内聚性

  同一个模块内的各个元素之间要高度紧密,可是各个模块之间的相互依存度却要不那么紧密。 内聚和耦合是密切相关的,同其余模块存在高耦合的模块意味着低内聚,而高内聚的模块意味着该模块同其余模块之间是低耦合。在进行软件设计时,应力争作到高内聚,低耦合。java

下降耦合

  一个模块访问另外一个模块的内容; 模块间传递数据结构为而不是单一数据算法

加强内聚

  逻辑上独立模块; 时间上同时执行的语句能够组合; 模块的元素只与该模块功能相关,模块之间按照顺序使用其输出结果做为输入结果安全

多线程介绍

更多1.http://www.javashuo.com/article/p-mwdqdxmu-a.html数据结构

2.http://www.cnblogs.com/xingele0917/p/3623162.html多线程

  Java提供了线程类Thread来建立多线程的程序。其实,建立线程与建立普通的类的对象的操做是同样的,而线程就是Thread类或其子类的实例对象。每一个Thread对象描述了一个单独的线程。要产生一个线程,有两种方法:测试

◆须要从Java.lang.Thread类派生一个新的线程类,重载它的run()方法; 
◆实现Runnalbe接口,重载Runnalbe接口中的run()方法。优化

  为何Java要提供两种方法来建立线程呢?它们都有哪些区别?相比而言,哪种方法更好呢?ui

  在Java中,类仅支持单继承,也就是说,当定义一个新的类的时候,它只能扩展一个外部类.这样,若是建立自定义线程类的时候是经过扩展 Thread类的方法来实现的,那么这个自定义类就不能再去扩展其余的类,也就没法实现更加复杂的功能。所以,若是自定义类必须扩展其余的类,那么就可使用实现Runnable接口的方法来定义该类为线程类,这样就能够避免Java单继承所带来的局限性。this

  还有一点最重要的就是使用实现Runnable接口的方式建立的线程能够处理同一资源,从而实现资源的共享.

   线程的状态:

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

 建立并运行线程:

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

 

线程安全与锁

更多1.http://www.javashuo.com/article/p-hmeqidde-n.html

  2.http://www.javashuo.com/article/p-ccpcxioi-bo.html    

  3.http://www.javashuo.com/article/p-omnlcwtn-cb.html

  因为考虑到多个线程同时抢占资源时,会发生冲突,这叫作线程安全问题,即咱们须要避免多个线程同时访问或修改同一资源,其实现机制为同步与互斥。避免冲突的方法就是对临界资源或者临界区加锁。

锁方法:

1.同步方法

1 在方法声明上加上synchronized 2 
3 public synchronized void method() { 4  可能会产生线程安全问题的代码 5 }  

  同步方法中锁的对象是 this(即调用者对象),并不必定是方法中用到的对象数据!

2.静态同步方法

1 在方法声明上加上static synchronized
2 
3 public static synchronized void method() {
4     可能会产生线程安全问题的代码
5 }

  静态同步方法中的锁对象是 类名.class(由于在加载类文件的时候,静态同步方法因为是静态的也被加载进内存了,类名.class的加载优先级高于静态方法)

3.同步代码块

1 在须要同步的代码外面包上一个synchronized
2 
3 synchronized(Object o) {
4     可能会产生线程安全问题的代码
5 } 

  同步代码块中锁的对象能够是任意对象(即Object o)

锁对象:

  this指当前调用者对象,通常状况下,普通方法块中是省略的(在构造方法中没有省略)。

死锁: 

   当前线程正在访问共享对象时,因为一个肯定的条件在某状况下不会发生,则该线程不释放锁,那么其他线程没法得到锁,也没法运行线程,多线程就会一直陷入等待中,等待这个“永远不会发生”的条件,这就会形成死锁。

原语操做和其余锁:

   经常使用的原语有信号量P/V操做、管程、共享内存等,是进程之间经常使用的通讯的方式。具体参考操做系统或原语、信号量。


 

synchronized是不错,但它并不完美。它有一些功能性的限制:

  1. 它没法中断一个正在等候得到锁的线程;
  2. 也没有有权利获得锁,只能一直陷入等待;即便不想等待,也就无法获得锁;
  3. 同步还要求锁的释放只能在与得到锁所在的堆栈帧相同的堆栈帧中进行,多数状况下,这没问题(并且与异常处理交互得很好),可是,确实存在一些非块结构的锁定更合适的状况。

  获取锁的线程释放锁只会有两种状况:

  一、获取锁的线程执行完了该代码块,而后线程释放对锁的占有。

  二、线程执行发生异常,此时JVM会让线程自动释放锁。


 

  其余锁

Lock与synchronized对比

  一、Lock不是Java语言内置的,synchronized是Java语言的关键字,所以是内置特性。Lock是一个类,经过这个类能够实现同步访问。

  二、synchronized不须要手动释放锁,当synchronized方法或者synchronized代码块执行完以后,系统会自动让线程释放对锁的占用;而Lock则必需要用户去手动释放锁,若是没有主动释放锁,就有可能致使出现死锁现象

因此lock是能够手动释放锁的。


 java.util.concurrent.locks包中经常使用的类和接口:

 1 public interface Lock {
 2     //用来获取锁。若是锁已被其余线程获取,则进行等待。
 3     void lock();
 4    // 当经过这个方法去获取锁时,若是线程正在等待获取锁,则这个线程可以响应中断,即中断线程的等待状态
 5     void lockInterruptibly() throws InterruptedException;
 6     //它表示用来尝试获取锁,若是获取成功,则返回true,若是获取失败(即锁已被其余线程获取),则返回false
 7     boolean tryLock();
 8     /*与tryLock()方法是相似的,只不过区别在于这个方法在拿不到锁时会等待必定的时间,在时间期限以内若是还拿不到锁,就返回false。
    若是若是一开始拿到锁或者在等待期间内拿到了锁,则返回true。*/
9 boolean tryLock(long time, TimeUnit unit) throws InterruptedException; 10 //释放锁 11 void unlock(); 12 Condition newCondition(); 13 }

  一、Lock与unlock

  Lock用于获取锁,但lock不会主动释放锁(调用unlock释放),因此须要与unlock()配合使用。通常在使用Lock时必须在try{}catch{}块中进行,而且将释放锁的操做放在finally块中进行,以保证锁必定被被释放,防止死锁的发生。

    PS:同一个线程能够连续得到同一把锁,但也必须释放相同次数的锁。

二、获取锁等待时间tryLock(long time, TimeUnit unit)

  若是你和朋友有约,在约定时间内对方未出现,我想你确定会扫兴的离去。对于线程来讲也应该时这样的,由于一般咱们是没法判断一个线程为何会没法得到锁,但咱们能够给该线程一个获取锁的时间限制,若是到时间尚未获取到锁,则放弃获取锁。


ReentrantLock增长了锁:

    1. void lock(); // 无条件的锁;

    2. void lockInterruptibly throws InterruptedException;//可中断的锁;

  解释: 使用ReentrantLock若是获取了锁当即返回,若是没有获取锁,当前线程处于休眠状态,直到得到锁或者当前线程能够被别的线程中断去作其余的事情;可是若是是synchronized的话,若是没有获取到锁,则会一直等待下去;

    3. boolean tryLock();//若是获取了锁当即返回true,若是别的线程正持有,当即返回false,不会等待;

  4. boolean tryLock(long timeout,TimeUnit unit);//若是获取了锁当即返回true,若是别的线程正持有锁,会等待参数给的时间,在等待的过程当中,若是获取锁,则返回true,若是等待超时,返回false。


Condition的特性:

  1.Condition中的await()方法至关于Object的wait()方法,Condition中的signal()方法至关于Object的notify()方法,Condition中的signalAll()至关于Object的notifyAll()方法。不一样的是,Object中的这些方法是和同步锁捆绑使用的;而Condition是须要与互斥锁/共享锁捆绑使用的。

  2.Condition它更强大的地方在于:可以更加精细的控制多线程的休眠与唤醒。对于同一个锁,咱们能够建立多个Condition,在不一样的状况下使用不一样的Condition。
  例如,假如多线程读/写同一个缓冲区:当向缓冲区中写入数据以后,唤醒"读线程";当从缓冲区读出数据以后,唤醒"写线程";而且当缓冲区满的时候,"写线程"须要等待;当缓冲区为空时,"读线程"须要等待。      

  若是采用Object类中的wait(), notify(), notifyAll()实现该缓冲区,当向缓冲区写入数据以后须要唤醒"读线程"时,不可能经过notify()或notifyAll()明确的指定唤醒"读线程",而只能经过notifyAll唤醒全部线程(可是notifyAll没法区分唤醒的线程是读线程,仍是写线程)。  可是,经过Condition,就能明确的指定唤醒读线程。 

 


 

线程调度

更多1.http://www.javashuo.com/article/p-tecfgclf-bz.html

2.http://www.javashuo.com/article/p-mwdqdxmu-a.html

  线程建立后,就要将它们运行起来,然而怎样调度怎样分配CPU给线程是一个问题。 

调度策略

  通常线程调度模式分为两种——抢占式调度和协同式调度。抢占式调度指的是每条线程执行的时间、线程的切换都由系统控制,系统控制指的是在系统某种运行机制下,可能每条线程都分一样的执行时间片,也多是某些线程执行的时间片较长,甚至某些线程得不到执行的时间片。在这种机制下,一个线程的堵塞不会致使整个进程堵塞。协同式调度指某一线程执行完后主动通知系统切换到另外一线程上执行,这种模式就像接力赛同样,一我的跑完本身的路程就把接力棒交接给下一我的,下我的继续往下跑。线程的执行时间由线程自己控制,线程切换能够预知,不存在多线程同步问题,但它有一个致命弱点:若是一个线程编写有问题,运行到一半就一直堵塞,那么可能致使整个系统崩溃。
---------------------
做者:超人汪小建(seaboat)
来源:CSDN
原文:https://blog.csdn.net/wangyangzhizhou/article/details/41122385

  但实际上咱们有时须要手动进行调整,就是按照自个人意愿去改变线程调度方法,让某一个线程具备优先权,每当该线程的条件知足就中止其它线程,而运行该线程。

 

调度优化及线程优先级

  优先级在各类常见算法中并很多见,咱们调度多线程能够利用优先级来肯定谁先谁后(操做系统中进程调度也有优先级调度方法)。理所固然,优先级高的线程会抢占CPU运行。

1 Java线程的优先级用整数表示,取值范围是1~10,Thread类有如下三个静态常量:
2 static int MAX_PRIORITY
3           线程能够具备的最高优先级,取值为10。
4 static int MIN_PRIORITY
5           线程能够具备的最低优先级,取值为1。
6 static int NORM_PRIORITY
7           分配给线程的默认优先级,取值为5。
   Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。
  每一个线程都有默认的优先级。主线程的默认优先级为Thread.NORM_PRIORITY。线程的优先级有继承关系,好比A线程中建立了B线程,那么B将和A具备相同的优先级。JVM提供了10个线程优先级,但与常见的操做系统都不能很好的映射。若是但愿程序能移植到各个操做系统中,应该仅仅使用Thread类有如下三个静态常量做为优先级,这样能保证一样的优先级采用了一样的调度方式。

线程切换

  线程睡眠:Thread.sleep(long millis)方法,使线程转到阻塞状态。millis参数设定睡眠的时间,以毫秒为单位。当睡眠结束后,就转为就绪(Runnable)状态。sleep()平台移植性好。

 
  线程等待:Object类中的wait()方法,致使当前的线程等待,直到其余线程调用此对象的 notify() 方法或 notifyAll() 唤醒方法。这个两个唤醒方法也是Object类中的方法,行为等价于调用 wait(0) 同样。
 
  线程让步:Thread.yield() 方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程。
 
  线程加入:join()方法,等待其余线程终止。在当前线程中调用另外一个线程的join()方法,则当前线程转入阻塞状态,直到另外一个进程运行结束,当前线程再由阻塞转为就绪状态。
 
  线程唤醒:Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。若是全部线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现作出决定时发生。线程经过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其余全部线程进行竞争;例如,唤醒的线程在做为锁定此对象的下一个线程方面没有可靠的特权或劣势。相似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的全部线程。

 

线程等待与唤醒

更多1.http://www.javashuo.com/article/p-tecfgclf-bz.html

2.http://www.javashuo.com/article/p-mwdqdxmu-a.html

wait与sleep 

  共同点:

1. 他们都是在多线程的环境下,均可以在程序的调用处阻塞指定的毫秒数,并返回。 
2. wait()和sleep()均可以经过interrupt()方法 打断线程的暂停状态 ,从而使线程马上抛出InterruptedException。 
   若是线程A但愿当即结束线程B,则能够对线程B对应的Thread实例调用interrupt方法。若是此刻线程B正在wait/sleep /join,则线程B会马上抛出InterruptedException,在catch() {} 中直接return便可安全地结束线程。 
   须要注意的是,InterruptedException是线程本身从内部抛出的,并非interrupt()方法抛出的。对某一线程调用 interrupt()时,若是该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。可是,一旦该线程进入到 wait()/sleep()/join()后,就会马上抛出InterruptedException 。 

不一样点:

  1.每一个对象都有一个锁来控制同步访问。Synchronized关键字能够和对象的锁交互,来实现线程的同步。sleep方法没有释放锁,而wait方法释放了锁,使得其余线程可使用同步控制块或者方法。

  2. wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep能够在任何地方使用 

  3. sleep必须捕获异常,而wait,notify和notifyAll不须要捕获异常

    因此sleep()和wait()方法的最大区别是:

    sleep()睡眠时,保持对象锁,仍然占有该锁;

    而wait()睡眠时,释放对象锁。 

  sleep()使当前线程进入停滞状态(阻塞当前线程),让出CUP的使用、目的是不让当前线程独自霸占该进程所获的CPU资源,以留必定时间给其余线程执行的机会;sleep()是Thread类的Static(静态)的方法;所以他不能改变对象的机锁,因此当在一个Synchronized块中调用Sleep()方法是,线程虽然休眠了,可是对象的机锁并木有被释放,其余线程没法访问这个对象(即便睡着也持有对象锁)。

  在sleep()休眠时间期满后,该线程不必定会当即执行,这是由于其它线程可能正在运行并且没有被调度为放弃执行,除非此线程具备更高的优先级。 
wait()方法是Object类里的方法;当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去(释放)了对象的机锁(暂时失去机锁,wait(long timeout)超时时间到后还须要返还对象锁);其余线程能够访问;
  wait()使用notify或者notifyAlll或者指定睡眠时间来唤醒当前等待池中的线程。
  wiat()必须放在synchronized block中,不然会在program runtime时扔出”java.lang.IllegalMonitorStateException“异常。

notify与notifyAll

   notify()只能唤醒一个在等待该对象(锁住的对象)线程,而notifyAll()唤醒全部在等待该对象的线程。

   Obj.wait(),与Obj.notify()/Obj.notifyAll()必需要与synchronized(Obj)一块儿使用,也就是wait,与notify是针对已经获取了Obj锁进行操做,从语法角度来讲就是Obj.wait(),Obj.notify必须在synchronized(Obj){...}语句块内。从功能上来讲wait就是说线程在获取对象锁后,主动释放对象锁,同时本线程休眠。直到有其它线程调用对象的notify()唤醒该线程,才能继续获取对象锁,并继续执行。相应的notify()就是对对象锁的唤醒操做。但有一点须要注意的是notify()调用后,并非立刻就释放对象锁的,而是在相应的synchronized(){}语句块执行结束,自动释放锁后,JVM会在wait()对象锁的线程中随机选取一线程,赋予其对象锁,唤醒线程,继续执行。这样就提供了在线程间同步、唤醒的操做。

相关文章
相关标签/搜索