java中多线程的同步与锁

来源:互联网 发布:java 字符串乱码 编辑:程序博客网 时间:2024/05/16 08:03
java线程的同步与锁:


(一)引进线程同步的原因:
当多个线程访问/修改同一个共享数据时,容易引发错误。
要保证线程同步互斥,所谓同步互斥就是:并发执行的多个线程在某一时间内只允许一个线程在执行以访问共享数据。
--------
(二)同步方法:Synchronized:


Java中每个对象都有一个内置同步锁。Java中可以使用synchronized关键字来取得一个对象的同步锁。
(1)同步方法与同步块:
1)同步方法:


   public syschronized void change()
   {
   }


2)同步语句:(因为效率问题,有时考虑使用同步语句块)


public void change() 
{
  Synchronized(this) 
  {}
}
3)自定义同步锁:想让一段代码同步,可能与当前对象没有什么关系
  private byte[] lock = new byte[1];
  public void change()
  {
     Synchronized(lock){}
  }


4)自定义锁要注意:  
   1. private ,防止在类外部应用改变。  
   2. 如果可能用到,重写get方法,返回对象的clone ,而不是本身。  


-----
(2)同步的实例:
http://blog.csdn.net/legend050709/article/details/39756051
-----
(3)Synchronized关键字的使用:
1)、只能同步方法和代码块,而不能同步变量和类。
只要保护好类中数据的安全访问和设置就可以了,不需要对类使用synchronized关键字,所以Java不允许这么做。并且想要同步数据,只需要对成员变量私有化,然后同步方法即可,不需要对成员变量使用synchronized,java也禁止这么做。
2)每个对象只有一个同步锁。
3)线程睡眠Thread.sleep();时,它所持的任何同步锁都不会释放。
------
(4)同步锁的释放时机:
1、同步方法或代码块正常结束
2、使用return或 break终止了执行,或者跑出了未处理的异常。
3、当线程执行同步方法或代码块时,程序执行了同步锁对象的wait()方法。


-----------------------
(三)线程的同步实现:
(1)线程同步控制主要通过:
   synchronized 
   wait()  notify()  notifyAll();
 (1.2)注意:
 1)wait notify 都是Object的方法,而不是Thread类的方法;
 2) 且只有拥有锁的线程才可以通过对象锁来调用wait,notify,notifyAll方法。
 3)wait方法与notify方法必须在同步块内执行,即synchronized(obj之内).
4)获得对象的锁:
 刚开始的时候是通过synchronized获得对象的锁,后来是通过notify来唤醒线程,并且锁可用,来获得对象的锁。

(2)wait():
对象锁调用wait方法,会释放掉该对象锁,同时当前线程休眠,进入等待队列;


(3)notify():
对象锁调用notify,唤醒该对象锁的等待队列中的第一个线程;
notify方法只应由获取锁的线程来调用。
(线程A通过调用对象锁obj的wait方法,会释放锁,同时线程A挂起;
需要其他线程(如线程B)来调用obj的notify来唤醒线程A.)


(4)notifyAll:
对象锁调用notifyAll,唤醒该对象锁的等待队列中所有线程;


(5)wait与sleep的区别:
1)sleep函数是Thread类的静态函数,不涉及到线程间同步概念,仅仅为了让一个线程自身获得一段沉睡时间。sleep可以在任何地方使用。
2)wait函数是object类的函数,要解决的问题是线程间的同步,该过程包含了同步锁的获取和释放,调用wait方法将会将调用者的线程挂起,直到其他线程调用同一个对象的notify方法才会重新激活调用者。
3)注意其他线程调用同一个对象的notify方法,唤醒线程后,唤醒的线程只有锁没有被占用时,才可以获得锁继续执行同步块或同步函数;如果唤醒后,锁被其他线程占用,则仍然需要等待。线程必须重新获得对像锁才能继续执行.因为synchronized代码块内没有锁是寸步不能走的




-----------


(四)进程间同步之生产者消费者:


package com.homer.thread;


public class waitnotify {
public static void main(String[] args) {
Q q = new Q();
new Producer(q);
new Consumer(q);
}
}


class Producer implements Runnable {
Q q = null;

public Producer(Q q) {
this.q = q;
(new Thread(this, "Producer")).start();
}

@Override
public void run() {
int i = 0;
while(i<5) {
q.put(i++);
}
}
}


class Consumer implements Runnable {
Q q = null;

public Consumer(Q q) {
this.q = q;
(new Thread(this, "Consumer")).start();
}


@Override
public void run() {
while(q.get()<5){
}
}
}


class Q {
int n;
boolean valueSet = false;

public synchronized int get() {
if(!valueSet) {// if valueSet == false,wait else try to got value
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}


System.out.println("Get n : " + n);
valueSet = false;
notify();

return n;
}

public synchronized void put(int n) {
if(valueSet) {// if valueSet == true,already have value so wait fetch,else put 
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}

this.n = n;
System.out.println("Put n : " + n);
valueSet = true;
notify();
}

}

----
(2)范例二:
package com.tao.test;


public class ThreadA {
    public static void main(String[] args) {
    RunnableTest myRunnanle=new RunnableTest();
       new Thread(myRunnanle).start();
        synchronized (myRunnanle) {
            try {
                System.out.println("第一步");
                myRunnanle.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("第四步");
        }
    }
}
 class RunnableTest implements Runnable {
    public void run() {
    try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
        synchronized (this) {
            System.out.println("第二步");
            notify();
            System.out.println("第三步");
        }
    }
}


分析:
1、因为子线程启动后,调用了sleep,所以主线程先进入同步代码块,而子线程之后因为没有锁,会进入阻塞状态。


2、主线程的同步代码块执行,打印第一句话,然后调用wait方法,进入等待状态。因为进入了等待状态,所以释放掉了锁,所以子线程可以获得锁,开始执行。


3、子线程执行,打印第二句话,然后调用notify方法,将主线程唤醒。可是子线程并没有结束,依然持有锁,所以主线程不得不进入阻塞状态,等待这个锁。


4、子线程打印第三句话,然后线程正常运行结束,释放掉锁。然后主线程得到了锁,从阻塞进入运行状态,打印第四句话。


--------

0 0