前面我分享了Synchronized的使用,当一个线程访问一个对象的Synchronized方法或者代码块的时候,就持有了锁,除非执行完或者遇到异常(发生异常JVM虚拟机会自动释放锁),才能释放锁,但是如果在执行代码块里sleep了或者有一些耗时很久的操作,那么锁就一直不释放,其他线程就会一直等待下去,Lock可以不让其他线程一直无限等待下去,另外一种情况,当有多个线程读写文件的时候,读和写会发生冲突,写和写会发生冲突,读和读按理应该不会发生冲突,但是如果用Synchronized的话,读和读也会发生冲突,ReadWriteLock可以解决这个问题,有一个点需要强调下,Synchronized是java内置语言,Lock不是,当一个线程执行完Synchronized修饰的方法或代码块之后,JVM会自动释放锁,但是Lock不会,必须手动执行lock.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();
}
}
开始调用了lock.lock得到锁,然后同步代码放到try catch中,在finally里执行lock.unlock释放锁,执行结果如下:
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,也就说这个方法无论如何都会立即返回。在拿不到锁时不会一直在那等待,执行结果如下:
A得到了锁
B没有得到锁
A释放了锁
3 tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false。如果如果一开始拿到锁或者在等待期间内拿到了锁,则返回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调用threadB.interrupt()方法能够中断线程B的等待过程。执行结果如下:
A得到了锁
B被中断
下面来看读写锁ReadWriteLock,可以实现读和读不冲突:
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();
}
}
执行结果如下:
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修饰的话,会发现是冲突的。
接下来说下Condition:Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition,Conditon中的await()对应Object的wait();Condition中的signal()对应Object的notify();Condition中的signalAll()对应Object的notifyAll(),接下来看个例子,重写生产者与消费者:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/*
* 生产者与消费者问题
*/
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("生产了" + 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();
}
}
}
/*
* 生产者
*/
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()的写法很类似。
以上如有问题,欢迎指正,谢谢。