Lock同步锁
用于解决多线程安全问题的方式:
-
同步代码块,synchronized实现,隐式锁
-
同步方法,synchronized实现,隐式锁
-
同步锁Lock:jdk 1.5以后
注:是一个显示锁,需要通过lock()方法上锁,必须通过unlock()方法进行释放锁(一定要将unlock()放到finally中,保证一定会释放锁),更加灵活
示例:卖票
package com.atguigu.juc;
public class TestLock {
public static void main(String[] args) {
SellTicketWindow sellTicketWindow = new SellTicketWindow();
new Thread(sellTicketWindow, "1号窗口").start();
new Thread(sellTicketWindow, "2号窗口").start();
new Thread(sellTicketWindow, "3号窗口").start();
}
}
class SellTicketWindow implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (ticket > 0){
System.out.println(Thread.currentThread().getName() + "已售票" + --ticket);
}
}
}
出现了线程安全问题
引入Lock
package com.atguigu.juc;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
SellTicketWindow sellTicketWindow = new SellTicketWindow();
new Thread(sellTicketWindow, "1号窗口").start();
new Thread(sellTicketWindow, "2号窗口").start();
new Thread(sellTicketWindow, "3号窗口").start();
}
}
class SellTicketWindow implements Runnable{
private int ticket = 100;
// 手动上锁、释放锁,首先需要有一把锁
private Lock lock = new ReentrantLock();
@Override
public void run() {
lock.lock();//上锁
try {
while (ticket > 0){
System.out.println(Thread.currentThread().getName() + "已售票" + --ticket);
}
}finally {
// 为保证释放锁操作一定执行,需要放到finally中
lock.unlock();
}
}
}
通过Lock完成等待唤醒机制
生产者消费者案例
package com.atguigu.juc;
/**
* 售货员从啤酒厂上货,张三买货
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor productor = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(productor, "啤酒厂").start();
new Thread(consumer, "张三").start();
}
}
/**
* 售货员
*/
class Clerk{
private int product = 0;
public synchronized void get(){
if (product >= 10){
System.out.println("产品已满!");
}else {
System.out.println("从" + Thread.currentThread().getName() + "上新货 : " + ++product);
}
}
public synchronized void sale(){
if (product <= 0){
System.out.println("缺货!");
}else {
System.out.println(Thread.currentThread().getName() + "买了啤酒" + product--);
}
}
}
/**
* 啤酒厂
*/
class Productor implements Runnable{
private Clerk clerk;
Productor(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.clerk.get();
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable{
private Clerk clerk;
Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.clerk.sale();
}
}
}
wait和notifyAll实现的等待唤醒机制
package com.atguigu.juc;
/**
* 售货员从啤酒厂上货,张三买货
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor productor = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(productor, "啤酒厂").start();
new Thread(consumer, "张三").start();
}
}
/**
* 售货员
*/
class Clerk{
private int product = 0;
public synchronized void get(){
if (product >= 10){
System.out.println("产品已满!");
try {
// 告知啤酒厂库存满了
this.wait();
} catch (InterruptedException e) {
}
}else {
System.out.println("从" + Thread.currentThread().getName() + "上新货 : " + ++product);
// 通知张三新货到了
this.notifyAll();
}
}
public synchronized void sale(){
if (product <= 0){
System.out.println("缺货!");
try {
// 告知张三没货了
this.wait();
} catch (InterruptedException e) {
}
}else {
System.out.println(Thread.currentThread().getName() + "买了啤酒" + product--);
// 通知啤酒厂,又卖掉货了,有空位了,可以生产啤酒了
this.notifyAll();
}
}
}
/**
* 啤酒厂
*/
class Productor implements Runnable{
private Clerk clerk;
Productor(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.clerk.get();
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable{
private Clerk clerk;
Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
this.clerk.sale();
}
}
}
但是这个案例还是存在问题的,有可能会产生死锁
减少库存上限和生产者生产前增加延时
package com.atguigu.juc;
/**
* 售货员从啤酒厂上货,张三买货
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor productor = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(productor, "啤酒厂").start();
new Thread(consumer, "张三").start();
}
}
/**
* 售货员
*/
class Clerk{
private int product = 0;
public synchronized void get(){
if (product >= 1){
System.out.println("产品已满!");
try {
// 告知啤酒厂库存满了
this.wait();
} catch (InterruptedException e) {
}
}else {
System.out.println("从" + Thread.currentThread().getName() + "上新货 : " + ++product);
// 通知张三新货到了
this.notifyAll();
}
}
public synchronized void sale(){
if (product <= 0){
System.out.println("缺货!");
try {
// 告知张三没货了
this.wait();
} catch (InterruptedException e) {
}
}else {
System.out.println(Thread.currentThread().getName() + "买了啤酒" + product--);
// 通知啤酒厂,又卖掉货了,有空位了,可以生产啤酒了
this.notifyAll();
}
}
}
/**
* 啤酒厂
*/
class Productor implements Runnable{
private Clerk clerk;
Productor(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
this.clerk.get();
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable{
private Clerk clerk;
Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
this.clerk.sale();
}
}
}
程序一直在运行中,结束不了
分析过程如下
解决上述问题的方法是去掉这里的else分支
调整后分析如下
上述问题解决
目前看这样是没问题的,但当生产者和消费者线程增加时,又会出现新的问题
其他代码不变,仅增加一个生产者线程和消费者线程
package com.atguigu.juc;
/**
* 售货员从啤酒厂上货,张三买货
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor productor = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(productor, "啤酒厂").start();
new Thread(consumer, "张三").start();
new Thread(productor, "烟厂").start();
new Thread(consumer, "李四").start();
}
}
/**
* 售货员
*/
class Clerk{
private int product = 0;
public synchronized void get(){
if (product >= 1){
System.out.println("产品已满!");
try {
// 告知啤酒厂库存满了
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println("从" + Thread.currentThread().getName() + "上新货 : " + ++product);
// 通知张三新货到了
this.notifyAll();
}
public synchronized void sale(){
if (product <= 0){
System.out.println("缺货!");
try {
// 告知张三没货了
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + "买了啤酒" + product--);
// 通知啤酒厂,又卖掉货了,有空位了,可以生产啤酒了
this.notifyAll();
}
}
/**
* 啤酒厂
*/
class Productor implements Runnable{
private Clerk clerk;
Productor(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
this.clerk.get();
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable{
private Clerk clerk;
Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
this.clerk.sale();
}
}
}
出现了线程安全问题
这是因为出现了虚假唤醒
的情况,分析如下
上述问题在JDK 1.7文档中给出了解决方法
使用while替换if,保证线程被唤醒后先去进行一次判断
完整代码如下
package com.atguigu.juc;
/**
* 售货员从啤酒厂上货,张三买货
*/
public class TestProductorAndConsumer {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Productor productor = new Productor(clerk);
Consumer consumer = new Consumer(clerk);
new Thread(productor, "啤酒厂").start();
new Thread(consumer, "张三").start();
new Thread(productor, "烟厂").start();
new Thread(consumer, "李四").start();
}
}
/**
* 售货员
*/
class Clerk{
private int product = 0;
public synchronized void get(){
while (product >= 1){
System.out.println("产品已满!");
try {
// 告知啤酒厂库存满了
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println("从" + Thread.currentThread().getName() + "上新货 : " + ++product);
// 通知张三新货到了
this.notifyAll();
}
public synchronized void sale(){
while (product <= 0){// 为了避免虚假环境问题,wait()应该总是被使用在while循环中
System.out.println("缺货!");
try {
// 告知张三没货了
this.wait();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + "买了啤酒" + product--);
// 通知啤酒厂,又卖掉货了,有空位了,可以生产啤酒了
this.notifyAll();
}
}
/**
* 啤酒厂
*/
class Productor implements Runnable{
private Clerk clerk;
Productor(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
}
this.clerk.get();
}
}
}
/**
* 消费者
*/
class Consumer implements Runnable{
private Clerk clerk;
Consumer(Clerk clerk){
this.clerk = clerk;
}
@Override
public void run() {
for (int i = 1; i <= 3; i++) {
this.clerk.sale();
}
}
}
以上就是完整的使用synchronized结合Object的wait和notify完成的等待唤醒机制
如何使用Lock同步锁完成等待唤醒机制
标签:JUC,product,同步,Thread,Lock,void,start,new,public From: https://www.cnblogs.com/wzzzj/p/18045240