问题引入:
车票问题。(多个窗口对车票这一对象进行操作)
每个对象都有一把锁,而一把锁只对应一个钥匙,只有当线程对象获取到对应锁的钥匙才可以对对象进行操作,只有唯一拥有钥匙的线程才可以进行资源的访问与操作。
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket11 ticket = new Ticket11();
Thread t1 = new Thread(ticket,"1号窗口");
Thread t2 = new Thread(ticket,"2号窗口");
Thread t3 = new Thread(ticket,"3号窗口");
t1.start();
t1.setPriority(1);
t2.start();
t2.setPriority(2);
t3.start();
t3.setPriority(10);
}
}
class Ticket11 implements Runnable{
int num = 10;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
// synchronized(this) {
if(num > 1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"剩余票数为: " + --num);
}
else {
break;
}
// }
}
}
}
1、 1 、2、3号窗口同时对票数进行操作。
2、 当票数为1时,3个窗口依然对票数进行操作,此时对于每一个进程来说票数的值为1,三个进程同时对车票进行操作、票数出现-1。
synchronized(同步):
1. 同步方法;
2. 同步代码块;
synchronized的作用:
1、当一个线程对象在调用同步方法时,所有其他调用此方法的线程都将被阻塞,知道当前方法执行完毕。
2、当一个线程对象在对某一字段进行读改操作时,所有其他对此实例操作的对象都将进入阻塞状态,直至当前操作结束。
3、当一个同步方法退出后,会自动建立与后续调用的同步方法(相同对象)的一个happens-before关系,保证所有线程对对象状态的修改可见。
将可能产生线程安全问题的资源(变量或者方法)使用synchronized关键字进行修饰,使其变为同步资源。
优化案例代码。
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket11 ticket = new Ticket11();
Thread t1 = new Thread(ticket,"1号窗口");
Thread t2 = new Thread(ticket,"2号窗口");
Thread t3 = new Thread(ticket,"3号窗口");
t1.start();
t1.setPriority(1);
t2.start();
t2.setPriority(2);
t3.start();
t3.setPriority(10);
}
}
class Ticket11 implements Runnable{
int num = 10;
@Override
public void run() {
// TODO Auto-generated method stub
synchronized(this){
while(true){
// synchronized(this) {
if(num > 1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"剩余票数为: " + --num);
}
else {
break;
}
// }
}
}
}
}
jdk1.5之后新增的。
Lock是锁的根接口之一,Lock代表实现类是ReentrantLock(可重入锁)。
优化案例代码:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Ticket11 ticket = new Ticket11();
Thread t1 = new Thread(ticket,"1号窗口");
Thread t2 = new Thread(ticket,"2号窗口");
Thread t3 = new Thread(ticket,"3号窗口");
t1.start();
t1.setPriority(1);
t2.start();
t2.setPriority(2);
t3.start();
t3.setPriority(10);
}
}
class Ticket11 implements Runnable{
int num = 100;
Lock lock = new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
lock.lock();
// synchronized(this) {
try {
if(num > 1) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"剩余票数为: " + --num);
}else break;
}finally {
lock.unlock();
}
// }
}
}
}
Lock接口可以实现的六个方法。
// 获取锁
void lock()
// 如果当前线程未被中断,则获取锁,可以响应中断
void lockInterruptibly()
// 返回绑定到此 Lock 实例的新 Condition 实例
Condition newCondition()
// 仅在调用时锁为空闲状态才获取该锁,可以响应中断
boolean tryLock()
// 如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁
boolean tryLock(long time, TimeUnit unit)
// 释放锁
void unlock()
(1)、该对象的代码块儿执行结束。
(2)、由于代码抛出异常而导致jvm释放锁。
(3)、由于该线程使用了wait()方法导致锁被释放。
wait()、notify()与notifyAll():
1、三种方法均为本地方法、且被finally修饰,无法被重写。
2、调用wait()方法可以使当前线程进入阻塞状态并且释放当前对象的锁。前提是当前线程拥有此对象的锁。
3、调用notify()方法,可以使被wait()方法放入阻塞状态的线程重新唤醒。只能唤醒一个线程。
4、调用notifyAll()方法可以唤醒所有正处于阻塞状态的线程全部唤醒。
注意事项:
1、由于wait()方法需要获取对象锁,所以wait()方法只能在同步方法内调用。
2、notify()与notifyAll()方法只能保证处于阻塞状态的线程被唤醒,而具体执行哪个线程由CPU的调度决定。
····不是23种设计模式之一。
产生数据的模块叫做生产者,处理数据的模块叫做消费者。数据从生产结束到消费的过程中临时存放的区域叫做缓冲区。
示例:
···饭店后厨炒菜是生产者,传菜员将菜从后厨送到客人桌子上,此时传菜员看做缓冲区,客人把菜吃了,成为消费者。
可以使用缓冲区来暂时存放数据,使得生产者与消费者更好的实现同步机制,减少空间与时间的浪费,提高运行效率
当缓冲区未达到某一状态时,消费者处于等待状态,生产者处于唤醒状态。
当缓冲区达到某一状态时,消费者被唤醒,生产者进入等待状态。
代码实现:
import java.util.LinkedList;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Warehouse warehouse = new Warehouse();
Thread t1 = new Thread(new product(warehouse),"1号生产者");
Thread t2 = new Thread(new product(warehouse),"2号生产者");
Thread t3 = new Thread(new product(warehouse),"3号生产者");
Thread p1 = new Thread(new com(warehouse),"1号消费者");
Thread p2 = new Thread(new com(warehouse),"2号消费者");
t1.start();
t2.start();
t3.start();
p1.start();
p2.start();
}
}
class Warehouse{
int MAX_WAREHOUSE = 10;
private LinkedList<Object> list = new LinkedList<>();
public void Produce() {
synchronized(list){
while(list.size() == MAX_WAREHOUSE) {
System.out.println(Thread.currentThread().getName()+"仓库已满!");
try {
list.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
list.add(new Object());
System.out.println(Thread.currentThread().getName()+"生产了" + list.size()+ "个产品");
list.notifyAll();
}
}
public void Constumer() {
synchronized(list) {
while(list.size() == 0) {
System.out.println(Thread.currentThread().getName() + "仓库已空,请生产!");
try {
list.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
list.remove();
System.out.println(Thread.currentThread().getName()+"消费了1个产品,还剩余"+list.size()+"个产品");
list.notifyAll();
}
}
}
class product implements Runnable{
Warehouse warehouse;
public product(){}
public product(Warehouse Warehouse) {
this.warehouse = Warehouse;
}
int i = 0;
@Override
public void run() {
// TODO Auto-generated method stub
while(i < 20) {
try {
Thread.sleep(100);
i++;
warehouse.Produce();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class com implements Runnable{
Warehouse warehouse;
public com() {}
public com(Warehouse warehouse) {
this.warehouse = warehouse;
}
int i = 0;
@Override
public void run() {
// TODO Auto-generated method stub
while(i < 10) {
try {
Thread.sleep(300);
i++;
warehouse.Constumer();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
PS:下一篇,JAVA中的数据结构。继续坚持!