Java中多线程总结(下)

来源:互联网 发布:c语言access violation 编辑:程序博客网 时间:2024/05/21 10:08

java中多线程综合(下)

 

1:多线程并发

 

多线程并发是线程同步中比较常见的现象,java多线程为了避免多线程并发解决多线程共享数据同步问题提供了synchronized关键字

 

synchronized关键字:当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。

 

a.Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。

b.如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何

synchronized方法的。

c.如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的Class对象

,因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行

顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。

d. synchronized块,写法:

synchronized(object)

{

}

表示线程在执行的时候会对object对象上锁。

e.synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;synchronized块则是一种细粒度的并发控制,只会将块中的代码同步

,位于方法内、synchronized块之外的代码是可以被多个线程同时访问到的。

同步的线程状态图:

 

 

2:wait与notify

a.wait与notify方法都是定义在Object类中,而且是final的,因此会被所有的Java类所继承并且无法重写。这两个方法要求在调用时线程应该已经获得了对象的锁,因此

对这两个方法的调用需要放在synchronized方法或块当中。当线程执行了wait方法时,它会释放掉对象的锁。

b.另一个会导致线程暂停的方法就是Thread类的sleep方法,它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。

c.notify():唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个wait 方法,在对象的监视器上等待。

具有notify()和wait()的线程池

 

3.通过以下三种方法之一,线程可以成为此对象监视器的所有者:
 
     a.通过执行此对象的同步实例方法。

b.通过执行在此对象上进行同步的 synchronized语句的正文。

c.对于 Class类型的对象,可以通过执行该类的同步静态方法。

 

4.多个线程对同一个对象的成员变量和局部变量的影响
    关于成员变量与局部变量:如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成员变 量的改变会影响到另一个线程)。  如果一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响到其他的线程。

5.死锁的问题:

 

定义:线程1锁住了对象A的监视器,等待对象B的监视器,线程2锁住了对象B的监视器,等待对象A的监视器,就造成了死锁。

 

导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性访问权。当线程访问对象时,线程会给对象加锁。

Java中每个对象都有一把锁与之对应。但Java不提供单独的lock和unlock操作。下面笔者分析死锁的两个过程“上锁”和“锁死”。

 

(1) 上锁
    许多线程在执行中必须考虑与其他线程之间共享数据或协调执行状态,就需要同步机制。因此大多数应用程序要求线程互相通信来同步它们的动作,在Java 程序中最简单实

现同步的方法就是上锁。在 Java 编程中,所有的对象都有锁。线程可以使用synchronized 关键字来获得锁。在任一时刻对于给定的类的实例,方法或同步的代码块只能被一个

线程执行。这是因为代码在执行之前要求获得对象的锁。

为了防止同时访问共享资源,线程在使用资源的前后可以给该资源上锁和开锁。给共享变量上锁就使得Java 线程能够快速方便地通信和同步。某个线程若给一个对象上了锁

,就可以知道没有其他线程能够访问该对象。即使在抢占式模型中,其他线程也不能够访问此对象,直到上锁的线程被唤醒、完成工作并开锁。那些试图访问一个上锁对象的线程

通常会进入睡眠状态,直到上锁的线程开锁。一旦锁被打开,这些睡眠进程就会被唤醒并移到准备就绪队列中。

 

(2)锁死
     如果程序中有几个竞争资源的并发线程,那么保证均衡是很重要的。系统均衡是指每个线程在执行过程中都能充分访问有限的资源,系统中没有饿死和死锁的线程。当多个并

发的线程分别试图同时占有两个锁时,会出现加锁冲突的情形。如果一个线程占有了另一个线程必需的锁,互相等待时被阻塞就有可能出现死锁。

面试题中的死锁事例

class LockA {
 public static final LockA a = newLockA();
}

class LockB {
 public static final LockB b = newLockB();
}

class Dead implements Runnable{
 private boolean flag;

 Dead(booleanflag) {
  this.flag =flag;
 }

 public voidrun() {
  while (true){
   if(flag) {
    synchronized(LockA.a) {
     System.out.println("if..........locka");
     synchronized(LockB.b) {
      System.out.println("if..........lockb");
     }
    }
   }else {
    synchronized(LockB.b) {
     System.out.println("if..........lockb");
     synchronized(LockA.a) {
      System.out.println("if..........locka");
     }
    }
   }
  }
 }
}

public class DeadLock {

 publicstatic void main(String[] args) {
  Dead d1 = newDead(false);
  Dead d2 = newDead(true);
  Thread t1 = newThread(d1);
  Thread t2 = newThread(d2);
  t1.start();
  t2.start();
     }

}

 

0 0
原创粉丝点击