同步 和 并发 : 对于程序员来说,他们其实是有两个意思的,一个是多线程层面,一个是网站请求服务器层面。
对于网站的请求层面来说:
同步:就是代码一步一步的有顺序的向下执行。
并发:就是多个请求同时访问一台服务器。
对于多线程层面来说:
同步: 就是加锁,为了保证数据的原子性。也就是保证数据的正确性。
并发:就是多个线程,操作同一个共享的资源。
所以:他们之前是有区别的,不是一个概念。
那么线程同步提高了效率吗 ?
没,降低了程序效率,因为程序中有阻塞,最主要的是抢锁耗费了很多资源,所以效率并不高。
接下来就说说Lock锁,他是jdk1.5提供并发包的一个工具。以前没有Lock锁的时候,大家都是用synchronized做锁。那为什么用Lock做锁呢 ?换句话说:就是Lock锁与synchronized的区别是什么 ?
用过synchronized的人都知道,synchronized(){} 这后面的{}就是需要被锁住的代码。他这个是自动的将包裹的代码全部上锁,执行到最后一个花括号就自动解锁。
而Lock锁,是手动上锁,手动解锁,相对来说更加灵活。而且效率也比synchronized的效率高,jdk1.5专门为多线程开发工具包,处理了很多效率问题,所以推荐使用。
区别:
1. Lock锁,可以尝试非阻塞式的获取锁,当线程获取到锁之后,就会持有锁,只要程序没有手动的释放锁,那么这个锁是绝对不会被释放的。还可以在任意位置,手动的释放锁。还可以指定时间范围内获取到锁,如果指定时间范围内,依然没有获取到锁,则返回,重新开始。所以Lock锁是相当灵活的。
2. synchronized是属于代码块,当代码执行完毕之后,或者中间出现异常,就会自动的释放锁资源,功能比较单一,且效率没有Lock锁高。
接下来,就用一下Lock做锁,并实现一下在使用Lock锁的情况下,怎么来完成线程之前的通讯。
public class Person {
public String username;
public int age;
/**
* true 生产者线程等待, false 消费者线程等待
*/
public boolean flag = false;
/**
* 重入锁
*/
public Lock lock = new ReentrantLock();
}
public class WriteThread extends Thread {
private Person person;
public Condition condition;
public WriteThread(Person person, Condition condition) {
this.person = person;
this.condition = condition;
}
@Override
public void run() {
/**
* 定义一个局部变量。
* 当这个值为偶数时,为Person类赋值: 偶数 0
* 当这个数为奇数时,为Person类赋值: 奇数 1
*/
int data = 0;
while (true) {
try {
//开启锁,注意这个锁的位置,一定要在上面。
person.lock.lock();
/**
* true: 等待消费
*/
if (person.flag) {
// TODO: await 用Lock锁的线程通讯.
// condition.await(); 其实就是 让当前线程阻塞挂起,等待其他线程使用 condition.signal(); 通知这里可以继续向下执行
condition.await();
}
if (data == 0) {
person.username = "偶数";
person.age = 0;
} else {
person.username = "奇数";
person.age = 1;
}
/**
* 依次改变data的值为: 偶数、奇数
*/
data = (data + 1) % 2;
person.flag = false;
// TODO: signal 用Lock锁的线程通讯。 condition.signal(); 其实就是提前唤醒阻塞的线程,可以完成,尝试非阻塞式的获取锁,提升一些效率。
condition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
/**
* 在finally中解锁。
*/
person.lock.unlock();
}
}
}
}
public class ReadThread extends Thread {
public Person person;
public Condition condition;
public ReadThread(Person person, Condition condition) {
this.person = person;
this.condition = condition;
}
@Override
public void run() {
while (true) {
try {
/**
* 上锁:
* 使用Lock锁之后,有个缺点,就是说,如果代码中间出现异常,如果没有特殊处理,下面的锁就一定不会解锁了。
* 那么处理这个问题也很简单,用try catch的finally。
*
* 注意这个锁的位置,一定要在上面。
*/
person.lock.lock();
if (!person.flag) {
// TODO: condition.await(); 其实就是 让当前线程阻塞挂起,等待其他线程使用 condition.signal(); 通知这里可以继续向下执行
condition.await();
}
System.out.println(person.username + "," + person.age);
person.flag = true;
// TODO: condition.signal(); 其实就是提前唤醒阻塞的线程,可以完成,尝试非阻塞式的获取锁,提升一些效率。
condition.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
/**
* 在finally中解锁。
*/
person.lock.unlock();
}
}
}
}
public class Test {
public static void main(String[] args) {
Person person = new Person();
/**
* 使用Lock锁后,线程之前通讯的方式用Condition类。
* Condition底层原理也是 使用wait 和 notify,不过调用的方法改了。
*/
Condition condition = person.lock.newCondition();
WriteThread writeThread = new WriteThread(person, condition);
ReadThread readThread = new ReadThread(person, condition);
writeThread.start();
readThread.start();
}
}