一、synchronized正确写法示例
public class ProducerConsumer {
// 仓库大小
private static int num = 100;
// 仓库货源数
private static int count = 0;
private static Object object = new Object();
public static void main(String[] args) {
// 实现Runable接口的生产者
Producer producer = new Producer();
// 实现Runable接口的消费者
Consumer consumer = new Consumer();
// 直接执行producer.run(),相当于执行普通的方法
// 而使用Thread.start()才会具有线程的特性,所以实现Runable的类必须再用new Thread()
Thread producerThread = new Thread(producer, "生产者");
Thread consumerThread = new Thread(consumer, "消费者");
// 使用start()的方法才具有线程的特性,执行run方法就会顺序执行
// producerThread.run();
producerThread.start();
consumerThread.start();
}
/**
* @author YKQ
* 生产者
*/
static class Producer implements Runnable{
/**
* 生产
*/
public void produce() {
// 两个类都使用object来加锁,则object锁了,会锁住持有object权限的线程
// object.wait()会使得当前持有object的类进入阻塞状态
// object.notify()会唤醒除了以object为锁的其他类
// wait()只会使当前类阻塞
// notify()只会唤醒当前类,因为,他相当于调用从父类Object继承过来的notify()方法
synchronized(object) {
for(int i=1;i<=1000;) {
if(count<num) {
count++;
System.out.println("生产第"+i+"件商品!仓库还剩"+count+"商品!");
i++;
object.notify();
} else {
try {
object.wait(1);
object.notify();
} catch (InterruptedException e) {
}
}
}
}
}
public void run() {
produce();
}
}
/**
* @author YKQ
* 消费者
*/
static class Consumer implements Runnable{
private int sum = 0;
/**
* 消费
*/
public void consume(){
synchronized (object) {
while(sum<=1000) {
if(count>0) {
sum++;
count--;
System.out.println("消费第"+sum+"商品!仓库还剩"+count+"商品!");
object.notify();
} else {
try {
object.wait(1);
object.notify();
} catch (InterruptedException e)
{
}
}
}
}
}
public void run() {
consume();
}
}
}
二、synchronized错误实例
示例一
此时调用的notify方法是this.notify().当前this是Producer,因此不会唤醒Consumer.
synchronized(object) {
for(int i=1;i<=1000;) {
if(count<num) {
count++;
System.out.println("生产第"+i+"件商品!仓库还剩"+count+"商品!");
i++;
// notify()相当于this.notify();然而synchronized的()不是this,是object
// synchronized块{}中的wait和notify以及notifyAll方法必须是()中的对象的方法。
// 比如Integer a = 1; synchronized(a){a.wait();a.notify(); }
// 或者synchronized(this){this.wait();this.notify()}
// 不然会报IllegalMonitorStateException
notify();
} else {
try {
wait();
notify();
} catch (InterruptedException e) {
}
}
}
}
示例二
Producer持有的锁是producer的this。Consumer持有的锁是Consumer的this。两者是不同对象,不会形成排队阻塞。
// 这样不会报Exception,但是生产者和消费者没有同步
// 虽然生产者和消费者synchronized()都是使用的this,但是两个this表示不同的含义
// Producer中的this表示的是Producer的对象
// Consumer中的this表示的是Consumer的对象
// 两个synchronized()想要同步必须是同一个锁
synchronized(this) {
for(int i=1;i<=1000;) {
if(count<num) {
count++;
System.out.println("生产第"+i+"件商品!仓库还剩"+count+"商品!");
i++;
notify();
} else {
try {
wait();
notify();
} catch (InterruptedException e) {
}
}
}
}
示例三
public class ProducerConsumer {
// 仓库大小
private static int num = 100;
// 仓库货源数
private static Integer count = 0;
public static void main(String[] args) {
// 实现Runable接口的生产者
Producer producer = new Producer();
// 实现Runable接口的消费者
Consumer consumer = new Consumer();
// 直接执行producer.run(),相当于执行普通的方法
// 而使用Thread.start()才会具有线程的特性,所以实现Runable的类必须再用new Thread()
Thread producerThread = new Thread(producer, "生产者");
Thread consumerThread = new Thread(consumer, "消费者");
// 使用start()的方法才具有线程的特性,执行run方法就会顺序执行
// producerThread.run();
producerThread.start();
consumerThread.start();
}
/**
* @author YKQ
* 生产者
*/
static class Producer implements Runnable{
/**
* 生产
*/
public void produce() {
// 这个地方既规避了示例一也规避了示例二的问题,为什么还是报了Excetion
// 这是因为Integer count = 0; 这个地方使用到了封箱,将一个int封箱成Integer
// 然后synchronized(count),但是在{}内部对该对象count进行开箱后+1,再封箱
// 再封箱的过程导致count的对象已经改变,所以synchronized(count)中的count
// 和count.notify()的count,严格上说不是一个对象。
synchronized(count) {
for(int i=1;i<=1000;) {
if(count<num) {
count++;
System.out.println("生产第"+i+"件商品!仓库还剩"+count+"商品!");
i++;
count.notify();
} else {
try {
count.wait();
count.notify();
} catch (InterruptedException e) {
}
}
}
}
}
public void run() {
produce();
}
}
/**
* @author YKQ
* 消费者
*/
static class Consumer implements Runnable{
private int sum = 0;
/**
* 消费
*/
public void consume(){
synchronized (count) {
while(sum<=1000) {
if(count>0) {
sum++;
count--;
System.out.println("消费第"+sum+"商品!仓库还剩"+count+"商品!");
count.notify();
} else {
try {
count.wait();
count.notify();
} catch (InterruptedException e)
{
}
}
}
}
}
public void run() {
consume();
}
}
}
解析示例三的情况
public class HashTest {
// 根据对象的hash值,来判断该对象是否发生改变
// 下面的例子可以很好的说明,Integer在再装箱的过程中变成了另一个对象
// 同时也举例说明了String在拼接时会变成另一个对象。
public static void main(String[] args) {
// TODO Auto-generated method stub
Student student = new Student();
System.out.println(student.hashCode());
student.id = "123456";
System.out.println(student.hashCode());
student.name = "张三";
System.out.println(student.hashCode());
Integer a = 1;
System.out.println(a.hashCode());
a++;
System.out.println(a.hashCode());
String s = "123";
System.out.println(s.hashCode());
s += "456";
System.out.println(s.hashCode());
}
static class Student{
public String id;
public String name;
}
}
三、Lock与Condition正确写法示例
public class LockConditionTest {
// 总产量
private static int sum = 1000;
// 仓库大小
private static int capacity = 100;
// 当前生产数量
private static volatile int produceNum = 0;
// 当前消费数量
private static volatile int consumeNum = 0;
// 当前仓库数量
private static volatile int currNum = 0;
// 创建lock锁,构造两个条件
private static Lock lock = new ReentrantLock();
private static Condition condition1 = lock.newCondition();
private static Condition condition2 = lock.newCondition();
public static void main(String[] args) {
// 构造四个线程
Producer producer1 = new Producer();
Consumer consumer1 = new Consumer();
FutureTask<Integer> producerTask1 = new FutureTask<>(producer1);
FutureTask<Integer> consumerTask1 = new FutureTask<>(consumer1);
Thread producerThread1 = new Thread(producerTask1);
Thread consumerThread1 = new Thread(consumerTask1);
// 启动线程
producerThread1.start();
consumerThread1.start();
}
public static class Producer implements Callable<Integer> {
public Producer() {
}
@Override
public Integer call() {
while (produceNum < sum) {
boolean hasLock = lock.tryLock();
if (hasLock) {
try {
if (currNum < capacity) {
produceNum++;
currNum++;
System.out.println(Thread.currentThread() + "生产第" + produceNum + "个产品,仓库当前容量" + currNum);
} else {
System.out.println(Thread.currentThread() + "工厂已满");
condition1.await();
}
condition2.signalAll();
} catch (Exception e) {
System.out.println(Thread.currentThread() + "生产第" + produceNum + "个产品抛异常!");
} finally {
lock.unlock();
}
} else {
System.out.println(Thread.currentThread() + "生产第" + produceNum + "个产品阻塞!");
}
}
return produceNum;
}
}
public static class Consumer implements Callable<Integer> {
@Override
public Integer call() {
while (consumeNum < sum) {
boolean hasLock = lock.tryLock();
if (hasLock) {
try {
if (currNum > 0) {
consumeNum++;
currNum--;
System.out.println(Thread.currentThread() + "消费第" + consumeNum + "个产品,仓库当前容量" + currNum);
} else {
System.out.println(Thread.currentThread() + "仓库已消费完毕");
condition2.await();
}
condition1.signalAll();
} catch (Exception e) {
System.out.println(Thread.currentThread() + "消费第" + consumeNum + "个产品抛异常!");
} finally {
lock.unlock();
}
} else {
System.out.println(Thread.currentThread() + "消费第" + consumeNum + "个产品阻塞!");
}
}
return consumeNum;
}
}
}
四、Lock与Condition错误示例
示例一
没有整体放在循环中,导致没有获取锁后就不执行了。
public Integer call() {
boolean hasLock = lock.tryLock();
if (hasLock) {
try {
while (produceNum < sum) {
if (currNum < capacity) {
produceNum++;
currNum++;
System.out.println(Thread.currentThread() + "生产第" + produceNum + "个产品!");
} else {
System.out.println(Thread.currentThread() + "工厂已满");
condition1.await();
}
condition2.signalAll();
}
return produceNum;
} catch (Exception e) {
LOGGER.error(Thread.currentThread() + "生产第" + produceNum + "个产品抛异常!");
} finally {
lock.unlock();
}
return produceNum;
} else {
LOGGER.error(Thread.currentThread() + "生产第" + produceNum + "个产品阻塞!");
return produceNum;
}
}