how2j.cn


工具版本兼容问题
与synchronized类似的,lock也能够达到同步的效果


本视频是解读性视频,所以希望您已经看过了本知识点的内容,并且编写了相应的代码之后,带着疑问来观看,这样收获才多。 不建议一开始就观看视频



18分59秒
本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)



步骤 1 : 回忆 synchronized 同步的方式   
步骤 2 : 使用Lock对象实现同步效果   
步骤 3 : trylock方法   
步骤 4 : 线程交互   
步骤 5 : 总结Lock和synchronized的区别   
步骤 6 : 练习-借助Lock,把MyStack修改为线程安全的类   
步骤 7 : 答案-借助Lock,把MyStack修改为线程安全的类   
步骤 8 : 练习-借助tryLock 解决死锁问题   
步骤 9 : 答案-借助tryLock 解决死锁问题   
步骤 10 : 练习-生产者消费者问题   
步骤 11 : 答案-生产者消费者问题   

步骤 1 :

回忆 synchronized 同步的方式

edit
首先回忆一下 synchronized 同步对象的方式

当一个线程占用 synchronized 同步对象,其他线程就不能占用了,直到释放这个同步对象为止
回忆 synchronized 同步的方式
package multiplethread; import java.text.SimpleDateFormat; import java.util.Date; public class TestThread { public static String now(){ return new SimpleDateFormat("HH:mm:ss").format(new Date()); } public static void main(String[] args) { final Object someObject = new Object(); Thread t1 = new Thread(){ public void run(){ try { System.out.println( now()+" t1 线程已经运行"); System.out.println( now()+this.getName()+ " 试图占有对象:someObject"); synchronized (someObject) { System.out.println( now()+this.getName()+ " 占有对象:someObject"); Thread.sleep(5000); System.out.println( now()+this.getName()+ " 释放对象:someObject"); } System.out.println(now()+" t1 线程结束"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t1.setName(" t1"); t1.start(); Thread t2 = new Thread(){ public void run(){ try { System.out.println( now()+" t2 线程已经运行"); System.out.println( now()+this.getName()+ " 试图占有对象:someObject"); synchronized (someObject) { System.out.println( now()+this.getName()+ " 占有对象:someObject"); Thread.sleep(5000); System.out.println( now()+this.getName()+ " 释放对象:someObject"); } System.out.println(now()+" t2 线程结束"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t2.setName(" t2"); t2.start(); } }
步骤 2 :

使用Lock对象实现同步效果

edit
Lock是一个接口,为了使用一个Lock对象,需要用到

Lock lock = new ReentrantLock();

synchronized (someObject) 类似的,lock()方法,表示当前线程占用lock对象,一旦占用,其他线程就不能占用了。
synchronized 不同的是,一旦synchronized 块结束,就会自动释放对someObject的占用。 lock却必须调用unlock方法进行手动释放,为了保证释放的执行,往往会把unlock() 放在finally中进行。
使用Lock对象实现同步效果
package multiplethread; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TestThread { public static String now() { return new SimpleDateFormat("HH:mm:ss").format(new Date()); } public static void log(String msg) { System.out.printf("%s %s %s %n", now() , Thread.currentThread().getName() , msg); } public static void main(String[] args) { Lock lock = new ReentrantLock(); Thread t1 = new Thread() { public void run() { try { log("线程启动"); log("试图占有对象:lock"); lock.lock(); log("占有对象:lock"); log("进行5秒的业务操作"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } finally { log("释放对象:lock"); lock.unlock(); } log("线程结束"); } }; t1.setName("t1"); t1.start(); try { //先让t1飞2秒 Thread.sleep(2000); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } Thread t2 = new Thread() { public void run() { try { log("线程启动"); log("试图占有对象:lock"); lock.lock(); log("占有对象:lock"); log("进行5秒的业务操作"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } finally { log("释放对象:lock"); lock.unlock(); } log("线程结束"); } }; t2.setName("t2"); t2.start(); } }
synchronized 是不占用到手不罢休的,会一直试图占用下去。
与 synchronized 的钻牛角尖不一样,Lock接口还提供了一个trylock方法。
trylock会在指定时间范围内试图占用,占成功了,就啪啪啪。 如果时间到了,还占用不成功,扭头就走~

注意: 因为使用trylock有可能成功,有可能失败,所以后面unlock释放锁的时候,需要判断是否占用成功了,如果没占用成功也unlock,就会抛出异常
trylock方法
package multiplethread; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TestThread { public static String now() { return new SimpleDateFormat("HH:mm:ss").format(new Date()); } public static void log(String msg) { System.out.printf("%s %s %s %n", now() , Thread.currentThread().getName() , msg); } public static void main(String[] args) { Lock lock = new ReentrantLock(); Thread t1 = new Thread() { public void run() { boolean locked = false; try { log("线程启动"); log("试图占有对象:lock"); locked = lock.tryLock(1,TimeUnit.SECONDS); if(locked){ log("占有对象:lock"); log("进行5秒的业务操作"); Thread.sleep(5000); } else{ log("经过1秒钟的努力,还没有占有对象,放弃占有"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if(locked){ log("释放对象:lock"); lock.unlock(); } } log("线程结束"); } }; t1.setName("t1"); t1.start(); try { //先让t1飞2秒 Thread.sleep(2000); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } Thread t2 = new Thread() { public void run() { boolean locked = false; try { log("线程启动"); log("试图占有对象:lock"); locked = lock.tryLock(1,TimeUnit.SECONDS); if(locked){ log("占有对象:lock"); log("进行5秒的业务操作"); Thread.sleep(5000); } else{ log("经过1秒钟的努力,还没有占有对象,放弃占有"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { if(locked){ log("释放对象:lock"); lock.unlock(); } } log("线程结束"); } }; t2.setName("t2"); t2.start(); } }
使用synchronized方式进行线程交互,用到的是同步对象的wait,notify和notifyAll方法

Lock也提供了类似的解决办法,首先通过lock对象得到一个Condition对象,然后分别调用这个Condition对象的:await, signal,signalAll 方法

注意: 不是Condition对象的wait,nofity,notifyAll方法,是await,signal,signalAll
线程交互
package multiplethread; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TestThread { public static String now() { return new SimpleDateFormat("HH:mm:ss").format(new Date()); } public static void log(String msg) { System.out.printf("%s %s %s %n", now() , Thread.currentThread().getName() , msg); } public static void main(String[] args) { Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); Thread t1 = new Thread() { public void run() { try { log("线程启动"); log("试图占有对象:lock"); lock.lock(); log("占有对象:lock"); log("进行5秒的业务操作"); Thread.sleep(5000); log("临时释放对象 lock, 并等待"); condition.await(); log("重新占有对象 lock,并进行5秒的业务操作"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } finally { log("释放对象:lock"); lock.unlock(); } log("线程结束"); } }; t1.setName("t1"); t1.start(); try { //先让t1飞2秒 Thread.sleep(2000); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } Thread t2 = new Thread() { public void run() { try { log("线程启动"); log("试图占有对象:lock"); lock.lock(); log("占有对象:lock"); log("进行5秒的业务操作"); Thread.sleep(5000); log("唤醒等待中的线程"); condition.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { log("释放对象:lock"); lock.unlock(); } log("线程结束"); } }; t2.setName("t2"); t2.start(); } }
步骤 5 :

总结Lock和synchronized的区别

edit
1. Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现,Lock是代码层面的实现。

2. Lock可以选择性的获取锁,如果一段时间获取不到,可以放弃。synchronized不行,会一根筋一直获取下去。 借助Lock的这个特性,就能够规避死锁,synchronized必须通过谨慎和良好的设计,才能减少死锁的发生。

3. synchronized在发生异常和同步块结束的时候,会自动释放锁。而Lock必须手动释放, 所以如果忘记了释放锁,一样会造成死锁。
步骤 6 :

练习-借助Lock,把MyStack修改为线程安全的类

edit Or  姿势不对,事倍功半! 点击查看做练习的正确姿势
练习-线程安全的MyStack 练习中,使用synchronized把MyStack修改为了线程安全的类。

接下来,借助Lock把MyStack修改为线程安全的类
步骤 7 :

答案-借助Lock,把MyStack修改为线程安全的类

edit
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
账号未激活 账号未激活,功能受限。 请点击激活
本视频是解读性视频,所以希望您已经看过了本答案的内容,带着疑问来观看,这样收获才多。 不建议一开始就观看视频

1分9秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)


把synchronized去掉
使用lock占用锁
使用unlock释放锁
必须放在finally执行,万一heros.addLast抛出异常也会执行
package multiplethread; import java.util.LinkedList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import charactor.Hero; public class MyStack { LinkedList<Hero> heros = new LinkedList<Hero>(); Lock lock = new ReentrantLock(); //把synchronized去掉 public void push(Hero h) { try{ //使用lock占用锁 lock.lock(); heros.addLast(h); } finally{ //使用unlock释放锁 //必须放在finally执行,万一heros.addLast抛出异常也会执行 lock.unlock(); } } //把synchronized去掉 public Hero pull() { try{ //使用lock占用锁 lock.lock(); return heros.removeLast(); } finally{ //使用unlock释放锁 //必须放在finally执行,万一heros.removeLast();抛出异常也会执行 lock.unlock(); } } public Hero peek() { return heros.getLast(); } public static void main(String[] args) { } }
步骤 8 :

练习-借助tryLock 解决死锁问题

edit Or  姿势不对,事倍功半! 点击查看做练习的正确姿势
当多个线程按照不同顺序占用多个同步对象的时候,就有可能产生死锁现象。

死锁之所以会发生,就是因为synchronized 如果占用不到同步对象,就会苦苦的一直等待下去,借助tryLock的有限等待时间,解决死锁问题
步骤 9 :

答案-借助tryLock 解决死锁问题

edit
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
账号未激活 账号未激活,功能受限。 请点击激活
本视频是解读性视频,所以希望您已经看过了本答案的内容,带着疑问来观看,这样收获才多。 不建议一开始就观看视频

3分32秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)


答案-借助tryLock 解决死锁问题
package multiplethread; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class TestThread { public static void main(String[] args) throws InterruptedException { Lock lock_ahri = new ReentrantLock(); Lock lock_annie = new ReentrantLock(); Thread t1 = new Thread() { public void run() { // 占有九尾妖狐 boolean ahriLocked = false; boolean annieLocked = false; try { ahriLocked = lock_ahri.tryLock(10, TimeUnit.SECONDS); if (ahriLocked) { System.out.println("t1 已占有九尾妖狐"); // 停顿1000秒,另一个线程有足够的时间占有安妮 Thread.sleep(1000); System.out.println("t1 试图在10秒内占有安妮"); try { annieLocked = lock_annie.tryLock(10, TimeUnit.SECONDS); if (annieLocked) System.out.println("t1 成功占有安妮,开始啪啪啪"); else{ System.out.println("t1 老是占用不了安妮,放弃"); } } finally { if (annieLocked){ System.out.println("t1 释放安妮"); lock_annie.unlock(); } } } } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } finally { if (ahriLocked){ System.out.println("t1 释放九尾狐"); lock_ahri.unlock(); } } } }; t1.start(); Thread.sleep(100); Thread t2 = new Thread() { public void run() { boolean annieLocked = false; boolean ahriLocked = false; try {annieLocked = lock_annie.tryLock(10, TimeUnit.SECONDS); if (annieLocked){ System.out.println("t2 已占有安妮"); // 停顿1000秒,另一个线程有足够的时间占有安妮 Thread.sleep(1000); System.out.println("t2 试图在10秒内占有九尾妖狐"); try { ahriLocked = lock_ahri.tryLock(10, TimeUnit.SECONDS); if (ahriLocked) System.out.println("t2 成功占有九尾妖狐,开始啪啪啪"); else{ System.out.println("t2 老是占用不了九尾妖狐,放弃"); } } finally { if (ahriLocked){ System.out.println("t2 释放九尾狐"); lock_ahri.unlock(); } } } } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } finally { if (annieLocked){ System.out.println("t2 释放安妮"); lock_annie.unlock(); } } } }; t2.start(); } }
步骤 10 :

练习-生产者消费者问题

edit Or  姿势不对,事倍功半! 点击查看做练习的正确姿势
练习-生产者消费者问题这个练习中,是用wait(), notify(), notifyAll实现了。

接下来使用Condition对象的:await, signal,signalAll 方法实现同样的效果
步骤 11 :

答案-生产者消费者问题

edit
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
在查看答案前,尽量先自己完成,碰到问题再来查看答案,收获会更多
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
查看本答案会花费4个积分,您目前总共有点积分。查看相同答案不会花费额外积分。 积分增加办法 或者一次性购买JAVA 中级总计0个答案 (总共需要0积分)
账号未激活 账号未激活,功能受限。 请点击激活
本视频是解读性视频,所以希望您已经看过了本答案的内容,带着疑问来观看,这样收获才多。 不建议一开始就观看视频

5分35秒 本视频采用html5方式播放,如无法正常播放,请将浏览器升级至最新版本,推荐火狐,chrome,360浏览器 如果装有迅雷,播放视频呈现直接下载状态,请调整 迅雷系统设置-基本设置-启动-监视全部浏览器 (去掉这个选项)


package multiplethread; import java.util.LinkedList; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MyStack<T> { LinkedList<T> values = new LinkedList<T>(); Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); public void push(T t) { try { lock.lock(); while (values.size() >= 200) { try { condition.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } condition.signalAll(); values.addLast(t); } finally { lock.unlock(); } } public T pull() { T t=null; try { lock.lock(); while (values.isEmpty()) { try { condition.await(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } condition.signalAll(); t= values.removeLast(); } finally { lock.unlock(); } return t; } public T peek() { return values.getLast(); } }


HOW2J公众号,关注后实时获知布最新的教程和优惠活动,谢谢。


问答区域    
2019-04-30 代码错误,不理解
yxmaizkm



为什么我的输出是先push一段时间后再pull
public class test_lock{
	public static void main(String []args) {
		
		for(int i=0;i<2;i++) {
		Thread producer=new ProducerThread("producer"+i);
		producer.start();
	}
	for(int i=4;i<7;i++) {
		Thread consumer=new ConsumerThread("consumer"+i);
		consumer.start();
	}
}
	static LinkedList<Character> list_char=new LinkedList<>();
	static Lock lock=new ReentrantLock();
	static Condition condition=lock.newCondition();
	static Random random=new Random();
    static class ProducerThread extends Thread{
	String producer;
	public ProducerThread(String s) {
		producer=s;
	}
	public void run() {
		super.run();
		while(true) {
			try {
				lock.lock();
				Thread.sleep(100);
				while(list_char.size()>=200)
				{condition.await();}
				char c= (char)((int)('A')+random.nextInt(26));
				list_char.add(c);
				System.out.println("push"+c);
				condition.signalAll();
			} catch (InterruptedException e) {
				// TODO 自动生成的 catch 块
				e.printStackTrace();
			}
			finally {
				lock.unlock();
			}
		}
	}
}
	static class ConsumerThread extends Thread{
		String consumer;
		public ConsumerThread(String s) {
			consumer=s;
		}
		public void run() {
			super.run();
			while(true) {
				try {
					
					lock.lock();
					Thread.sleep(100);
					while(list_char.size()==0)
					{condition.await();}
					System.out.println("pull"+list_char.removeLast());
					condition.signalAll();
				} catch (InterruptedException e) {
					// TODO 自动生成的 catch 块
					e.printStackTrace();
				}
				finally {
					lock.unlock();
				}
		}
		}
}
}
pushN
pushZ
pushP
pushG
pushP
pushX
pushW
pushH
pushV
pushE
pushH
pushC
pushZ
pushR
pushS
pushI
pushC
pushN
pushP
pushN
pushH
pullH
pullN
pullP
pullN
pullC
pullI
pullS





回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2019-04-25 交作业(三),欢迎暂时没思路的朋友来交流参考
simple_曹泽



随时欢迎交流、指正,感谢! /* * 需求:用Condition对象的await/signal/signalAll方法实现如下需求 * 1、用栈来存取数据,设置当栈内为空时,pull等待,当栈内有200条数据时,push等待 * 2、提供一个生产者线程类(ProducerThread),生产随机大写字符压入栈内 * 3、提供一个消费者线程类(Consumer Thread),从栈中弹出字符,并打印 * 4、提供一个测试类,使2个生产者和3个消费者线程同时运行 * 注意:生产者、消费者线程需要不停的执行,必须在run方法中用一个while(true)循环,wait、notify方法必须放在synchronized块中 * 注意:Condition是辅助Lock使用的条件对象,因此Lock对象该上锁的、解锁的,仍然需要操作 * */ 个人学习博客:https://blog.csdn.net/hui1setouxiang 欢迎交流指正,内容是根据版主推荐的《Core Java》写的一些关键章节(多线程、lambda表达式、集合、IO流、内部类、String是final类等)的总结。 祝您生活顺心,学习愉快,共勉~
package thread;

import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import thread.ThreadSafetyStackDemo.*;

/*
 * 	需求:用Condition对象的await/signal/signalAll方法实现如下需求
 * 		1、用栈来存取数据,设置当栈内为空时,pull等待,当栈内有200条数据时,push等待
 * 		2、提供一个生产者线程类(ProducerThread),生产随机大写字符压入栈内
 * 		3、提供一个消费者线程类(Consumer Thread),从栈中弹出字符,并打印
 * 		4、提供一个测试类,使2个生产者和3个消费者线程同时运行
 * 	注意:生产者、消费者线程需要不停的执行,必须在run方法中用一个while(true)循环,wait、notify方法必须放在synchronized块中
 * 	注意:Condition是辅助Lock使用的条件对象,因此Lock对象该上锁的、解锁的,仍然需要操作
 * 	
 */
public class ProducerConsumerDemo2 {
	private static MyStack<Character> characters = new MyStack<>();
	private static Lock lock = new ReentrantLock();
	private static Condition condition = lock.newCondition();
	
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		for (int i = 1; i < 3; i++) {
			Thread producer = new ProducerThread(i);
			producer.start();
		}

		for (int i = 3; i < 6; i++) {
			Thread consumer = new ConsumerThread(i);
			consumer.start();
		}
	}

	static class ProducerThread extends Thread {

		private int id;
		public ProducerThread(int id) {
			this.id = id;
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			while (true) {
					try {
						lock.lock();
						System.out.println("producer线程" + id);
						while (characters.size() >= 200) {
							// 使用哪个来wait()?
							try {
								condition.await();
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
						characters.push((char) (65 + new Random().nextInt(26)));
						condition.signal();
					} finally {
						lock.unlock();
					}
					
				}
			}
		}

	static class ConsumerThread extends Thread {

		private int id;

		public ConsumerThread(int id) {
			this.id = id;
		}

		@Override
		public void run() {
			// TODO Auto-generated method stub
			super.run();
			while (true) {
				try {
					lock.lock();
					System.out.println("Consumer线程" + id);
					while (characters.size() == 0) {
						try {
							condition.await();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
					characters.pull();
					condition.signal();
				} finally {
					lock.unlock();
				}
			}
		}

	}
}

							





回答已经提交成功,正在审核。 请于 我的回答 处查看回答记录,谢谢
答案 或者 代码至少填写一项, 如果是自己有问题,请重新提问,否则站长有可能看不到





2019-04-01 对吗
2019-03-29 解决死锁问题
2019-03-28 作业一
2018-11-13 消费者生产者中无法同步
2018-10-27 作业
2018-09-07 同步对象问题
2018-08-07 死锁
2018-06-01 放代码
2018-05-18 在用lock实现生产者消费者模型的时候,建议在finally中的unlock方法前添加sleep(100),可以解决pull push和sysout不同步的问题
2018-05-17 在用lock解决死锁问题的时候,两个线程中间的sleep(1000)很重要
2018-04-12 最后释放锁完了,这两个布尔数不用重新设置为 false吗?
2017-12-24 练习3有异常
2017-09-08 我写lock这些的时候 提示要在前面加final
2017-04-26 boolean的问题
2017-04-10 站长更喜欢哪种同步方式呢
2017-03-29 rx
2017-02-06 步骤9的 Thread.sleep(1000);是1秒吧?




提问之前请登陆
提问已经提交成功,正在审核。 请于 我的提问 处查看提问记录,谢谢
关于 JAVA 中级-多线程-Lock对象 的提问

尽量提供截图代码异常信息,有助于分析和解决问题。 也可进本站QQ群交流: 1001964108
提问尽量提供完整的代码,环境描述,越是有利于问题的重现,您的问题越能更快得到解答。
对教程中代码有疑问,请提供是哪个步骤,哪一行有疑问,这样便于快速定位问题,提高问题得到解答的速度
在已经存在的几千个提问里,有相当大的比例,是因为使用了和站长不同版本的开发环境导致的,比如 jdk, eclpise, idea, mysql,tomcat 等等软件的版本不一致。
请使用和站长一样的版本,可以节约自己大量的学习时间。 站长把教学中用的软件版本整理了,都统一放在了这里, 方便大家下载: http://how2j.cn/k/helloworld/helloworld-version/1718.html

上传截图