前面我分享了Synchronized的使用,當(dāng)1個線程訪問1個對象的Synchronized方法或代碼塊的時候,就持有了鎖,除非履行完或遇到異常(產(chǎn)生異常JVM虛擬機會自動釋放鎖),才能釋放鎖,但是如果在履行代碼塊里sleep了或有1些耗時很久的操作,那末鎖就1直不釋放,其他線程就會1直等待下去,Lock可以不讓其他線程1直無窮等待下去,另外1種情況,當(dāng)有多個線程讀寫文件的時候,讀和寫會產(chǎn)生沖突,寫和寫會產(chǎn)生沖突,讀和讀按理應(yīng)當(dāng)不會產(chǎn)生沖突,但是如果用Synchronized的話,讀和讀也會產(chǎn)生沖突,ReadWriteLock可以解決這個問題,有1個點需要強調(diào)下,Synchronized是java內(nèi)置語言,Lock不是,當(dāng)1個線程履行完Synchronized修飾的方法或代碼塊以后,JVM會自動釋放鎖,但是Lock不會,必須手動履行l(wèi)ock.unLock()方法來釋放鎖,否則鎖就永久不會得到釋放。
1 Lock.lock,代碼以下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String args[]) {
LockObject lo = new LockObject();
MyThread t1 = new MyThread(lo);
MyThread t2 = new MyThread(lo);
Thread ta = new Thread(t1,"A");
Thread tb = new Thread(t2,"B");
ta.start();
tb.start();
}
}
class LockObject {
Lock lock = new ReentrantLock();
public void LockFuc() {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + "得到了鎖");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + "釋放了鎖");
}
}
}
class MyThread implements Runnable{
LockObject lo= null;
public MyThread(LockObject lo){
this.lo = lo;
}
@Override
public void run() {
// TODO Auto-generated method stub
lo.LockFuc();
}
}
開始調(diào)用了lock.lock得到鎖,然后同步代碼放到try catch中,在finally里履行l(wèi)ock.unlock釋放鎖,履行結(jié)果以下:
A得到了鎖
A釋放了鎖
B得到了鎖
B釋放了鎖
2 lock.tryLock(),代碼以下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String args[]) {
LockObject lo = new LockObject();
MyThread t1 = new MyThread(lo);
MyThread t2 = new MyThread(lo);
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t2, "B");
ta.start();
tb.start();
}
}
class LockObject {
Lock lock = new ReentrantLock();
public void LockFuc() {
if (lock.tryLock()) {
try {
System.out.println(Thread.currentThread().getName() + "得到了鎖");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + "釋放了鎖");
}
}else{
System.out.println(Thread.currentThread().getName() + "沒有得到鎖");
}
}
}
class MyThread implements Runnable {
LockObject lo = null;
public MyThread(LockObject lo) {
this.lo = lo;
}
@Override
public void run() {
// TODO Auto-generated method stub
lo.LockFuc();
}
}
tryLock()方法是有返回值的,它表示用來嘗試獲得鎖,如果獲得成功,則返回true,如果獲得失敗(即鎖已被其他線程獲得),則返回false,也就說這個方法不管如何都會立即返回。在拿不到鎖時不會1直在那等待,履行結(jié)果以下:
A得到了鎖
B沒有得到鎖
A釋放了鎖
3 tryLock(long time, TimeUnit unit)方法和tryLock()方法是類似的,只不過區(qū)分在于這個方法在拿不到鎖時會等待1定的時間,在時間期限以內(nèi)如果還拿不到鎖,就返回false。如果如果1開始拿到鎖或在等待期間內(nèi)拿到了鎖,則返回true。
4 lockInterruptibly(),代碼以下:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String args[]) {
LockObject lo = new LockObject();
MyThread t1 = new MyThread(lo);
MyThread t2 = new MyThread(lo);
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t2, "B");
ta.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
tb.start();
tb.interrupt();
}
}
class LockObject {
Lock lock = new ReentrantLock();
public void LockFuc() throws InterruptedException {
lock.lockInterruptibly();
try {
System.out.println(Thread.currentThread().getName() + "得到了鎖");
while(true){
;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println(Thread.currentThread().getName() + "釋放了鎖");
}
}
}
class MyThread implements Runnable {
LockObject lo = null;
public MyThread(LockObject lo) {
this.lo = lo;
}
@Override
public void run() {
// TODO Auto-generated method stub
try{
lo.LockFuc();
}catch(InterruptedException e){
System.out.println(Thread.currentThread().getName()+"被中斷");
}
}
}
lock.lockInterruptibly()想獲得某個鎖時,假若此時線程A獲得到了鎖,而線程B只有在等待,那末對線程B調(diào)用threadB.interrupt()方法能夠中斷線程B的等待進程。履行結(jié)果以下:
A得到了鎖
B被中斷
下面來看讀寫鎖ReadWriteLock,可以實現(xiàn)讀和讀不沖突:
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class LockTest {
public static void main(String args[]) {
LockObject lo = new LockObject();
MyThread t1 = new MyThread(lo);
MyThread t2 = new MyThread(lo);
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t2, "B");
ta.start();
tb.start();
}
}
class LockObject {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public void LockFuc(){
lock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName() + "得到了鎖");
for(int i=0;i<10;i++){
System.out.println(Thread.currentThread().getName() + "正在進行讀操作" + i);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
System.out.println(Thread.currentThread().getName() + "釋放了鎖");
}
}
}
class MyThread implements Runnable {
LockObject lo = null;
public MyThread(LockObject lo) {
this.lo = lo;
}
@Override
public void run() {
// TODO Auto-generated method stub
lo.LockFuc();
}
}
履行結(jié)果以下:
A得到了鎖
B得到了鎖
A正在進行讀操作0
B正在進行讀操作0
A正在進行讀操作1
B正在進行讀操作1
A正在進行讀操作2
B正在進行讀操作2
A正在進行讀操作3
B正在進行讀操作3
B正在進行讀操作4
B正在進行讀操作5
B正在進行讀操作6
B正在進行讀操作7
B正在進行讀操作8
B正在進行讀操作9
A正在進行讀操作4
A正在進行讀操作5
B釋放了鎖
A正在進行讀操作6
A正在進行讀操作7
A正在進行讀操作8
A正在進行讀操作9
A釋放了鎖
可以看到讀和讀其實不沖突,但是如果有Synchronized修飾的話,會發(fā)現(xiàn)是沖突的。
接下來講下Condition:Condition是在java 1.5中才出現(xiàn)的,它用來替換傳統(tǒng)的Object的wait()、notify()實現(xiàn)線程間的協(xié)作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()這類方式實現(xiàn)線程間協(xié)作更加安全和高效。因此通常來講比較推薦使用Condition,Conditon中的await()對應(yīng)Object的wait();Condition中的signal()對應(yīng)Object的notify();Condition中的signalAll()對應(yīng)Object的notifyAll(),接下來看個例子,重寫生產(chǎn)者與消費者:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 生產(chǎn)者與消費者問題
*/
public class ProduceConsume {
public static void main(String[] args) {
SyncStack ss = new SyncStack();
Produce pd = new Produce(ss);
Consume cs = new Consume(ss);
Thread t1 = new Thread(pd);
Thread t2 = new Thread(cs);
t1.start();
t2.start();
}
}
/*
* 饅頭實體類
*/
class ManTou {
private int id;
public ManTou(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "ManTou " + getId();
}
}
/*
* 饅頭框類
*/
class SyncStack {
Lock lock = new ReentrantLock();
Condition full = lock.newCondition();
Condition empty = lock.newCondition();
int index = 0;
ManTou[] mtArray = new ManTou[6];
public void push(ManTou mt) {
lock.lock();
try {
while (index == mtArray.length) {
try {
full.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
empty.signal();
mtArray[index] = mt;
index++;
System.out.println("生產(chǎn)了" + mt);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void pop() {
lock.lock();
try {
while (index == 0) {
try {
empty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
full.signal();
index--;
System.out.println("消費了" + mtArray[index]);
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/*
* 生產(chǎn)者
*/
class Produce implements Runnable {
SyncStack ss = null;
public Produce(SyncStack ss) {
this.ss = ss;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 20; i++) {
ManTou mt = new ManTou(i);
if (ss != null) {
ss.push(mt);
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/*
* 消費者
*/
class Consume implements Runnable {
SyncStack ss = null;
public Consume(SyncStack ss) {
this.ss = ss;
}
@Override
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 20; i++) {
if (ss != null) {
ss.pop();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
跟之前Object的wait()、notify()的寫法很類似。
以上如有問題,歡迎指正,謝謝。
上一篇 應(yīng)聘時漂亮的回答