Java多线程总结

时间:2022-02-25 04:33:28

一. 进程是执行中的程序,程序是静态的(我们写完以后不运行就一直放在那里),进程是执行中的程序,是动态概念的。一个进程可以有多个线程。

二. 多线程包含两个或两个以上并发运行的部分,把程序中每个这样并发运行的部分称为线程。

  1. 基于进程的多任务处理是指:允许你的计算机同时运行两个或更多的程序。

  2. 基于线程的多任务处理是指:一个程序可以执行两个或者更多的任务。

三. Java线程的实现方式:

  1. extends Thread 并重写run()方法;

  2. implements Runnable接口,把实现runnable接口的对象作为参数传递给new Thread(new Runnable(){public void run() {}});

  3. 当没有给线程设定名字时,线程的名字是Thread-number(number会自增长),并被所有Thread共享,因为thread的name是static的成员变量;

  启动一个线程调用start()方法,不是run()方法!!!!!!start()方法首先为执行线程准备好系统资源,然后再去调用Runnable接口的run()方法。当某各类继承Thread或者实现Runnable接口后,该类就叫一个线程类。

注意:start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的。
从程序运行的结果可以发现,多线程程序是乱序执行。因此,只有乱序执行的代码才有必要设计为多线程。
Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。
实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。

四. 停止一个运行中的线程不要使用stop()方法,用一个变量去控制。

Java多线程总结

五. 线程的生命周期:

下面的这个图非常重要!你如果看懂了这个图,那么对于多线程的理解将会更加深刻!

Java多线程总结

 

  一个线程要运行,必须同时满足两个条件:1.获取资源;2.获取CPU(不可控制);

  1. 新建状态(New):新创建了一个线程对象, 但不为其分配资源
  2. 可运行状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法。 为其分配资源,该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权。
  3. 运行状态(Running):就绪状态的 线程获取了CPU,执行程序代码。
  4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。阻塞的情况分三种:
    (1). 等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。(wait会释放持有的锁)
    (2). 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池中。
    (3). 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。(注意,sleep是不会释放持有的锁)
  5. 死亡状态(Dead):线程执行完了或者因异常退出了run()方法,该线程结束生命周期

六. 线程调度策略

Java多线程总结

七. 线程类成员变量和局部变量:

多个线程之间共享线程类的同一个成员变量,即任何一个线程修改成员变量后其他线程都会受到影响,多个线程共享同一份成员变量,对多线程来说,成员变量是不安全的;

多个线程之间不共享线程类的局部变量,每个线程都有一份自己的局部变量,对多线程来说局部变量是安全的;

八. 线程同步

Java中每个对象都有一个锁或者叫监视器,当访问每个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法访问该synchronized方法了,直到原先线程执行方法完毕(或者抛出异常)后释放锁资源,其他线程才可访问该方法。

1、synchronized关键字的作用域有二种: 
  1)是某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法; 

  2)是某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法,它锁的是对象对应的Class(一个Class不管有多少对象,它们都是指向同一个Class的),它可以对类的所有对象起作用。 
2、除了方法前用synchronized关键字,synchronized关键字还可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问。用法是: synchronized(this){/*区块*/},它的作用域是当前对象; 
3、synchronized关键字是不能继承的,也就是说,基类的方法synchronized f(){} 在继承类中并不自动是synchronized f(){},而是变成了f(){}。继承类需要你显式的指定它的某个方法为synchronized方法; 

Java对多线程的支持与同步机制深受大家的喜爱,似乎看起来使用了synchronized关键字就可以轻松地解决多线程共享数据同步问题。到底如何?――还得对synchronized关键字的作用进行深入了解才可定论。

总的说来,synchronized关键字可以作为函数的修饰符,也可作为函数内的语句,也就是平时说的同步方法和同步语句块。如果再细的分类,synchronized可作用于instance变量、object reference(对象引用)、static函数和class literals(类名称字面常量)身上。

在进一步阐述之前,我们需要明确几点:

A.无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。

B.每个对象只有一个锁(lock)与之相关联。

C.实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。

接着来讨论synchronized用到不同地方对代码产生的影响:

假设P1、P2是同一个类的不同对象,这个类中定义了以下几种情况的同步块或同步方法,P1、P2就都可以调用它们。

1.把synchronized当作函数修饰符时,示例代码如下:

Public synchronized void methodAAA() {

  //….

}

这也就是同步方法,那这时synchronized锁定的是哪个对象呢?它锁定的是调用这个同步方法对象。也就是说,当一个对象P1在不同的线程中执行这个同步方法时,它们之间会形成互斥,达到同步的效果。但是这个对象所属的Class所产生的另一对象P2却可以任意调用这个被加了synchronized关键字的方法。

上边的示例代码等同于如下代码:

public void methodAAA() {

  synchronized (this) {     //  (1)

       //…..

  }

}

 (1)处的this指的是什么呢?它指的就是调用这个方法的对象,如P1。可见同步方法实质是将synchronized作用于object reference。――那个拿到了P1对象锁的线程,才可以调用P1的同步方法,而对P2而言,P1这个锁与它毫不相干,程序也可能在这种情形下摆脱同步机制的控制,造成数据混乱:(

2.同步块,示例代码如下:

public void method3(SomeObject so) {

  synchronized(so) {

         //…..

  }

}

这时,锁就是so这个对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的instance变量(它得是一个对象)来充当锁:

class Foo implements Runnable {

  private byte[] lock = new byte[0];  // 特殊的instance变量

  Public void methodA() {

    synchronized(lock) { //… }

  }

  //…..

}

注:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。

3.将synchronized作用于static 函数,示例代码如下:

Class Foo {

  public synchronized static void methodAAA()  {// 同步的static 函数

    //….

  }

  public void methodBBB() {

    synchronized(Foo.class)   //  class literal(类名称字面常量)

  }

}

代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。

记得在《Effective Java》一书中看到过将 Foo.class和 P1.getClass()用于作同步锁还不一样,不能用P1.getClass()来达到锁这个Class的目的。P1指的是由Foo类产生的对象。

可以推断:如果一个类中定义了一个synchronized的static函数A,也定义了一个synchronized 的instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。A方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。

1、线程同步的目的是为了保护多个线程反问一个资源时对资源的破坏。
2、线程同步方法是通过锁来实现,每个对象都有切仅有一个锁,这个锁与一个特定的对象关联,线程一旦获取了对象锁,其他访问该对象的线程就无法再访问该对象的其他非同步方法。
3、对于静态同步方法,锁是针对这个类的,锁对象是该类的Class对象。静态和非静态方法的锁互不干预。一个线程获得锁,当在一个同步方法中访问另外对象上的同步方法时,会获取这两个对象锁。
4、对于同步,要时刻清醒在哪个对象上同步,这是关键。
5、编写线程安全的类,需要时刻注意对多个线程竞争访问资源的逻辑和安全做出正确的判断,对“原子”操作做出分析,并保证原子操作期间别的线程无法访问竞争资源。
6、当多个线程等待一个对象锁时,没有获取到锁的线程将发生阻塞。
7、死锁是线程间相互等待锁锁造成的,在实际中发生的概率非常的小。真让你写个死锁程序,不一定好使,呵呵。但是,一旦程序发生死锁,程序将死掉。
推荐阅读:http://www.jianshu.com/p/40d4c7aebd66