JavaSe基础XX15——线程_3
2014-08-26 10:39
471 查看
回顾:编写一个死锁的例子:
*23-多线程(线程间通信-示例)
线程间通讯:
多个线程在处理同一资源,但是任务却不同。
比如:水塘,一个进水管一个出水管。
出现了线程安全问题。
要考虑两个问题:1.是否有共享数据;2.是否有多条语句。
解决就要同步代码块。
考虑同步的前提:一个锁内有多个线程。而且多线程之间的锁必须是同一个。
所以不既不能在类中新建使用的单独的Object;也不能用this。
那可以放什么呢?
1. Input.class 字节码文件; 2. r 文件 3.Resource.class
改好的代码如下:
此时的输出结果:
一大片一大片,为什么?
是因为Input拿到cpu的执行权之后,会进行多次的操作,之前赋值的话,都会被最后一次赋值给覆盖。而Output拿到cpu执行权之后也会多次的输出,所以就会执行多次。
因此,我们会看到一大片一大片的输出。但这不是我们想看到的结果。
我们想每一次输入之后,就输出对应的结果,即结果是交叉的那种。那该怎么办呢?
*24-多线程(线程间通信-等待唤醒机制)
wait() --------- notify()
用的频率很高!
等待/唤醒机制。
涉及的方法:
1,wait(): 让线程处于冻结状态,被wait的线程会被存储到线程池中。会释放CPU的执行权和执行资格的。
2,notify():唤醒线程池中一个线程(任意).
3,notifyAll():唤醒线程池中的所有线程。让线程具备执行资格。
这些方法都必须定义在同步中。
因为这些方法是用于操作线程状态的方法。
必须要明确到底操作的是哪个锁上的线程。
为什么操作线程的方法wait notify notifyAll定义在了Object类中?
因为这些方法是监视器的方法。监视器其实就是锁。
锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中。
改好之后的程序:
结果输出:
为什么要对wait和sleep进行try...catch?到底是什么引起的异常的?后面会讲。
*25-多线程(线程间通信-等待唤醒机制-代码优化)
我们发现上面的代码,有这样的问题。Resource里面的属性,很容易被外部访问,这不安全。所以要在Resource类中提供访问的方法。
代码优化如下:(实际开发也是这样的)
*26-多线程(线程间通信-多生产者多消费者问题)
小作坊例子:
多任务多线程:多生产多消费
问题一:
问题二:
为什么在多任务多线程的时候会出现这样的安全隐患?
注意代码解释。
改正代码之后:
但上面的程序又会造成死锁的情况,
*27-多线程(线程间通信-多生产者多消费者问题解决)
该怎么解决死锁问题呢?
是因为唤醒的本方。解决方案是,也要唤醒对方的线程。notify是唤醒的任意一方。
notifyall是唤醒所有的。
输入结果:
ok的。
图例解释:
----------------小结--------------------------
生产者,消费者。
多生产者,多消费者的问题。
if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。
while判断标记,解决了线程获取执行权后,是否要运行!
notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
notifyAll解决了本方线程一定会唤醒对方线程的问题。
注意:这种解决方法,会唤醒本方的线程,唤醒之后,还要判断while,所以不是很好。
但在后面的版本有新的解决方案。
*28-多线程(线程间通信-多生产者多消费者问题-JDK1.5新特性-Lock)
多生产,多消费——while和notifyall。
notifyall唤醒本方会降低了效率。
在新版的JDK1.5之后提供了工具。
是对synchronized的替代,把锁封装成对象。
对之前的代码进行修改替换:
暂不附上,下面一起附上。
*29-多线程(线程间通信-多生产者多消费者问题-JDK1.5新特性-Condition)
仿照第一次编辑的情况写Condition形式的代码。[即只有if和notify的样子] 如下:
同样如果把if换成while,signal换成signalall就和上版本一样了,解决所有问题(除了唤醒所有线程之外)
*30-多线程(线程间通信-多生产者多消费者问题-JDK1.5解决办法)
保证只唤醒对方的一个线程。
*31-多线程(线程间通信-多生产者多消费者问题-JDK1.5解决办法-范例)
---------------------------小结----------------------------------
条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像
作为一个示例,假定有一个绑定的缓冲区,它支持
(
注意,
除非另行说明,否则为任何参数传递
三种形式的条件等待(可中断、不可中断和超时)在一些平台上的实现以及它们的性能特征可能会有所不同。尤其是它可能很难提供这些特性和维护特定语义,比如排序保证。更进一步地说,中断线程实际挂起的能力在所有平台上并不是总是可行的。
因此,并不要求某个实现为所有三种形式的等待定义完全相同的保证或语义,也不要求其支持中断线程的实际挂起。
要求实现清楚地记录每个等待方法提供的语义和保证,在某个实现不支持中断线程的挂起时,它必须遵从此接口中定义的中断语义。
由于中断通常意味着取消,而又通常很少进行中断检查,因此实现可以先于普通方法的返回来对中断进行响应。即使出现在另一个操作后的中断可能会释放线程锁时也是如此。实现应记录此行为。
*32-多线程(wait和sleep的区别)
wait 和 sleep 区别?
1,wait可以指定时间也可以不指定。
sleep必须指定时间。
2,在同步中时,对cpu的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
谁拿锁,谁执行。
*33-多线程(停止线程方式-定义标记)
停止线程的方法1. stop方法。
那怎么停下来呢?
第二种方法2. run方法结束。
停止线程:
1,stop方法。
2,run方法结束。
怎么控制线程的任务结束呢?
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常就用定义标记来完成。
*34-多线程(停止线程方式-Interrupt)
但是用标记停止线程不是最好的。因为存在一种情况,他不能使得线程停下来。
下面的例子:
但是如果线程处于了冻结状态,无法读取标记。如何结束呢?
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。
当时强制动作会发生了InterruptedException,记得要处理。
*35-多线程(守护线程-setDaemon)
*36-多线程(其他方法-join等)
join用在哪?当我们在进行一个线程运算的时候,我们希望临时加进一个的时候,就要用join。
其他方法:
在讲线程组的概念:
另一个方法:
释放执行权。
*37-多线程(面试题)
出一个面试题:
第一题:
/*class Test implements Runnable
{
public void run(Thread t)
{}
}*/
//如果错误 错误发生在哪一行?错误在第一行,应该被abstract修饰
第二题:
package testThread; class Lock { public static final Object locka = new Object(); public static final Object lockb = new Object(); } class DeadLockDemo implements Runnable { private boolean flag; public DeadLockDemo(boolean flag) { this.flag = flag; } @Override public void run() { if (flag) { synchronized (Lock.locka) { System.out.println(Thread.currentThread().getName() + "...if...lock_a"); synchronized (Lock.lockb) { System.out.println(Thread.currentThread().getName() + "...if...lock_b"); } } } else { synchronized (Lock.lockb) { System.out.println(Thread.currentThread().getName() + "...else...lock_b"); synchronized (Lock.locka) { System.out.println(Thread.currentThread().getName() + "...if...lock_a"); } } } } } public class DeadLock { public static void main(String[] args) { DeadLockDemo d1 = new DeadLockDemo(false); DeadLockDemo d2 = new DeadLockDemo(true); Thread t1 = new Thread(d1); Thread t2 = new Thread(d2); t1.start(); t2.start(); } }
*23-多线程(线程间通信-示例)
线程间通讯:
多个线程在处理同一资源,但是任务却不同。
比如:水塘,一个进水管一个出水管。
/* 线程间通讯: 多个线程在处理同一资源,但是任务却不同。 */ //资源 class Resource { String name; String sex; } //输入 class Input implements Runnable { Resource r ; // Object obj = new Object(); Input(Resource r) { this.r = r; } public void run() { int x = 0; while(true) { //synchronized(r) //{ if(x==0) { r.name = "mike"; r.sex = "nan"; } else { r.name = "丽丽"; r.sex = "女女女女女女"; } //} x = (x+1)%2; } } } //输出 class Output implements Runnable { Resource r; // Object obj = new Object(); Output(Resource r) { this.r = r; } public void run() { while(true) { //synchronized(r) //{ System.out.println(r.name+"....."+r.sex); //} } } } class ResourceDemo { public static void main(String[] args) { //创建资源。 Resource r = new Resource(); //创建任务。 Input in = new Input(r); Output out = new Output(r); //创建线程,执行路径。 Thread t1 = new Thread(in); Thread t2 = new Thread(out); //开启线程 t1.start(); t2.start(); } }注:上例,为了保证Input和Output是同一个对象。就采用了传对象的方法。同样也可以将Resource写成单例模式。但这样不好。
出现了线程安全问题。
要考虑两个问题:1.是否有共享数据;2.是否有多条语句。
解决就要同步代码块。
考虑同步的前提:一个锁内有多个线程。而且多线程之间的锁必须是同一个。
所以不既不能在类中新建使用的单独的Object;也不能用this。
那可以放什么呢?
1. Input.class 字节码文件; 2. r 文件 3.Resource.class
改好的代码如下:
package testThread; class Resource { String name; String sex; } class Input implements Runnable { Resource r; public Input(Resource r) { this.r = r; } @Override public void run() { int x = 0; while (true) { synchronized (r) { if (x == 0) { r.name = "Mike"; r.sex = "男"; } else { r.name = "Jane"; r.sex = "女"; } x = (++x) % 2; } } } } class Output implements Runnable { Resource r; public Output(Resource r) { this.r = r; } @Override public void run() { while (true) { synchronized (r) { System.out.println(r.name + "..." + r.sex); } } } } public class ResourceDemo { public static void main(String[] args) { Resource r = new Resource(); Input in = new Input(r); Output out = new Output(r); Thread t1 = new Thread(in); Thread t2 = new Thread(out); t1.start(); t2.start(); } }
此时的输出结果:
一大片一大片,为什么?
是因为Input拿到cpu的执行权之后,会进行多次的操作,之前赋值的话,都会被最后一次赋值给覆盖。而Output拿到cpu执行权之后也会多次的输出,所以就会执行多次。
因此,我们会看到一大片一大片的输出。但这不是我们想看到的结果。
我们想每一次输入之后,就输出对应的结果,即结果是交叉的那种。那该怎么办呢?
*24-多线程(线程间通信-等待唤醒机制)
wait() --------- notify()
用的频率很高!
等待/唤醒机制。
涉及的方法:
1,wait(): 让线程处于冻结状态,被wait的线程会被存储到线程池中。会释放CPU的执行权和执行资格的。
2,notify():唤醒线程池中一个线程(任意).
3,notifyAll():唤醒线程池中的所有线程。让线程具备执行资格。
这些方法都必须定义在同步中。
因为这些方法是用于操作线程状态的方法。
必须要明确到底操作的是哪个锁上的线程。
为什么操作线程的方法wait notify notifyAll定义在了Object类中?
因为这些方法是监视器的方法。监视器其实就是锁。
锁可以是任意的对象,任意的对象调用的方式一定定义在Object类中。
改好之后的程序:
package testThread; class Resource { String name; String sex; boolean flag = false; } class Input implements Runnable { Resource r; public Input(Resource r) { this.r = r; } @Override public void run() { int x = 0; while (true) { synchronized (r) { if (r.flag) try { r.wait(); } catch (InterruptedException e) { e.printStackTrace(); } if (x == 0) { r.name = "Mike"; r.sex = "男"; } else { r.name = "Jane"; r.sex = "女"; } r.flag = true; r.notify(); } x = (++x) % 2; } } } class Output implements Runnable { Resource r; public Output(Resource r) { this.r = r; } @Override public void run() { while (true) { synchronized (r) { if (!r.flag) try { r.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(r.name + "..." + r.sex); r.flag = false; r.notify(); } } } } public class ResourceDemo { public static void main(String[] args) { Resource r = new Resource(); Input in = new Input(r); Output out = new Output(r); Thread t1 = new Thread(in); Thread t2 = new Thread(out); t1.start(); t2.start(); } }
结果输出:
为什么要对wait和sleep进行try...catch?到底是什么引起的异常的?后面会讲。
*25-多线程(线程间通信-等待唤醒机制-代码优化)
我们发现上面的代码,有这样的问题。Resource里面的属性,很容易被外部访问,这不安全。所以要在Resource类中提供访问的方法。
代码优化如下:(实际开发也是这样的)
package testThread; class Resource { private String name; private String sex; boolean flag = false; public synchronized void setInfo(String name, String sex) { if(flag) try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void show() { if(!flag) try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(name + "...+..." + sex); flag = false; this.notify(); } } class Input implements Runnable { Resource r; public Input(Resource r) { this.r = r; } @Override public void run() { int x = 0; while (true) { if (x == 0) { r.setInfo("Mike", "男"); } else { r.setInfo("Jane", "女"); } x = (++x) % 2; } } } class Output implements Runnable { Resource r; public Output(Resource r) { this.r = r; } @Override public void run() { while (true) { r.show(); } } } public class ResourceDemo { public static void main(String[] args) { Resource r = new Resource(); Input in = new Input(r); Output out = new Output(r); Thread t1 = new Thread(in); Thread t2 = new Thread(out); t1.start(); t2.start(); } }
*26-多线程(线程间通信-多生产者多消费者问题)
小作坊例子:
package testThread; class Res { private String name; private int count = 1; private boolean flag = false; public synchronized void setInfo(String name) { this.name = name + count; count++; if (flag) try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("...生产..." + this.name); flag = true; notify(); } public synchronized void showInfo() { if (!flag) try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("...消费......" + name); flag = false; notify(); } } class Producer implements Runnable { Res r; public Producer(Res r) { this.r = r; } @Override public void run() { while (true) { r.setInfo("烤鸭"); } } } class Consumer implements Runnable { Res r; public Consumer(Res r) { this.r = r; } @Override public void run() { while (true) { r.showInfo(); } } } public class ProducerConsumerDemo { public static void main(String[] args) { Res r = new Res(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); t1.start(); t2.start(); } }
多任务多线程:多生产多消费
问题一:
问题二:
为什么在多任务多线程的时候会出现这样的安全隐患?
注意代码解释。
改正代码之后:
package testThread; class Res { private String name; private int sum = 0; private boolean flag = false; public synchronized void setInfo(String name) { //t0 t1 while (flag) //t0 t1 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } this.name = name + sum; sum++; //1 System.out.println("...生产..." + this.name); //p1 p2 p3 flag = true; notify(); } public synchronized void showInfo() { //t2 t3 while (!flag)//t2 t3 try { this.wait();} catch (InterruptedException e) { e.printStackTrace(); } System.out.println("...消费......" + name);//c1 flag = false; notify(); } } class Producer implements Runnable { Res r; public Producer(Res r) { this.r = r; } @Override public void run() { while (true) { r.setInfo("烤鸭"); } } } class Consumer implements Runnable { Res r; public Consumer(Res r) { this.r = r; } @Override public void run() { while (true) { r.showInfo(); } } } public class ProducerConsumerDemo { public static void main(String[] args) { Res r = new Res(); Producer pro0 = new Producer(r); Producer pro1 = new Producer(r); Consumer con0 = new Consumer(r); Consumer con1 = new Consumer(r); Thread t0 = new Thread(pro0); Thread t1 = new Thread(pro1); Thread t2 = new Thread(con0); Thread t3 = new Thread(con1); t0.start(); t1.start(); t2.start(); t3.start(); } }
但上面的程序又会造成死锁的情况,
*27-多线程(线程间通信-多生产者多消费者问题解决)
该怎么解决死锁问题呢?
是因为唤醒的本方。解决方案是,也要唤醒对方的线程。notify是唤醒的任意一方。
notifyall是唤醒所有的。
package testThread; class Res { private String name; private int sum = 0; private boolean flag = false; public synchronized void setInfo(String name) { //t0 t1 while (flag) //t0 t1 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } this.name = name + sum; sum++; //1 System.out.println(Thread.currentThread().getName()+"...生产..." + this.name); //p1 p2 p3 flag = true; notifyAll(); } public synchronized void showInfo() { //t2 t3 while (!flag)//t2 t3 try { this.wait();} catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"...消费......" + name);//c1 flag = false; notifyAll(); } } class Producer implements Runnable { Res r; public Producer(Res r) { this.r = r; } @Override public void run() { while (true) { r.setInfo("烤鸭"); } } } class Consumer implements Runnable { Res r; public Consumer(Res r) { this.r = r; } @Override public void run() { while (true) { r.showInfo(); } } } public class ProducerConsumerDemo { public static void main(String[] args) { Res r = new Res(); Producer pro0 = new Producer(r); Producer pro1 = new Producer(r); Consumer con0 = new Consumer(r); Consumer con1 = new Consumer(r); Thread t0 = new Thread(pro0); Thread t1 = new Thread(pro1); Thread t2 = new Thread(con0); Thread t3 = new Thread(con1); t0.start(); t1.start(); t2.start(); t3.start(); } }
输入结果:
ok的。
图例解释:
----------------小结--------------------------
生产者,消费者。
多生产者,多消费者的问题。
if判断标记,只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。
while判断标记,解决了线程获取执行权后,是否要运行!
notify:只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。
notifyAll解决了本方线程一定会唤醒对方线程的问题。
注意:这种解决方法,会唤醒本方的线程,唤醒之后,还要判断while,所以不是很好。
但在后面的版本有新的解决方案。
*28-多线程(线程间通信-多生产者多消费者问题-JDK1.5新特性-Lock)
多生产,多消费——while和notifyall。
notifyall唤醒本方会降低了效率。
在新版的JDK1.5之后提供了工具。
是对synchronized的替代,把锁封装成对象。
对之前的代码进行修改替换:
暂不附上,下面一起附上。
*29-多线程(线程间通信-多生产者多消费者问题-JDK1.5新特性-Condition)
仿照第一次编辑的情况写Condition形式的代码。[即只有if和notify的样子] 如下:
package testThread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; class Res { private String name; private int sum = 0; private boolean flag = false; Lock lock = new ReentrantLock(); Condition c1 = lock.newCondition(); public void setInfo(String name) { lock.lock(); if (flag) try { c1.await(); } catch (InterruptedException e) { e.printStackTrace(); } this.name = name + sum; sum++; System.out.println(Thread.currentThread().getName() + "...生产..5.0." + this.name); // p1 p2 p3 flag = true; c1.signal(); lock.unlock(); } public void showInfo() { lock.lock(); if (!flag) try { c1.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "...消费...5.0..." + name);// c1 flag = false; c1.signal(); lock.unlock(); } } class Producer implements Runnable { Res r; public Producer(Res r) { this.r = r; } @Override public void run() { while (true) { r.setInfo("烤鸭"); } } } class Consumer implements Runnable { Res r; public Consumer(Res r) { this.r = r; } @Override public void run() { while (true) { r.showInfo(); } } } public class ProducerConsumerDemo { public static void main(String[] args) { Res r = new Res(); Producer pro0 = new Producer(r); Producer pro1 = new Producer(r); Consumer con0 = new Consumer(r); Consumer con1 = new Consumer(r); Thread t0 = new Thread(pro0); Thread t1 = new Thread(pro1); Thread t2 = new Thread(con0); Thread t3 = new Thread(con1); t0.start(); t1.start(); t2.start(); t3.start(); } }
同样如果把if换成while,signal换成signalall就和上版本一样了,解决所有问题(除了唤醒所有线程之外)
*30-多线程(线程间通信-多生产者多消费者问题-JDK1.5解决办法)
<pre name="code" class="java">import java.util.concurrent.locks.*; class Resource { private String name; private int count = 1; private boolean flag = false; // 创建一个锁对象。 Lock lock = new ReentrantLock(); //通过已有的锁获取该锁上的监视器对象。 // Condition con = lock.newCondition(); //通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。 Condition producer_con = lock.newCondition(); Condition consumer_con = lock.newCondition(); public void set(String name)// t0 t1 { lock.lock(); try { while(flag) // try{lock.wait();}catch(InterruptedException e){}// t1 t0 try{producer_con.await();}catch(InterruptedException e){}// t1 t0 this.name = name + count;//烤鸭1 烤鸭2 烤鸭3 count++;//2 3 4 System.out.println(Thread.currentThread().getName()+"...生产者5.0..."+this.name);//生产烤鸭1 生产烤鸭2 生产烤鸭3 flag = true; // notifyAll(); // con.signalAll(); consumer_con.signal(); } finally { lock.unlock(); } } public void out()// t2 t3 { lock.lock(); try { while(!flag) // try{this.wait();}catch(InterruptedException e){} //t2 t3 try{cousumer_con.await();}catch(InterruptedException e){} //t2 t3 System.out.println(Thread.currentThread().getName()+"...消费者.5.0......."+this.name);//消费烤鸭1 flag = false; // notifyAll(); // con.signalAll(); producer_con.signal(); } finally { lock.unlock(); } } } class Producer implements Runnable { private Resource r; Producer(Resource r) { this.r = r; } public void run() { while(true) { r.set("烤鸭"); } } } class Consumer implements Runnable { private Resource r; Consumer(Resource r) { this.r = r; } public void run() { while(true) { r.out(); } } } class ProducerConsumerDemo2 { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
保证只唤醒对方的一个线程。
*31-多线程(线程间通信-多生产者多消费者问题-JDK1.5解决办法-范例)
---------------------------小结----------------------------------
/* jdk1.5以后将同步和锁封装成了对象。 并将操作锁的隐式方式定义到了该对象中, 将隐式动作变成了显示动作。 Lock接口: 出现替代了同步代码块或者同步函数。将同步的隐式锁操作变成现实锁操作。 同时更为灵活。可以一个锁上加上多组监视器。 lock():获取锁。 unlock():释放锁,通常需要定义finally代码块中。 Condition接口:出现替代了Object中的wait notify notifyAll方法。 将这些监视器方法单独进行了封装,变成Condition监视器对象。 可以任意锁进行组合。 await(); signal(); signalAll(); */
public interface Condition
Condition将
Object监视器方法(
wait、
notify和
notifyAll)分解成截然不同的对象,以便通过将这些对象与任意
Lock实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,
Lock替代了
synchronized方法和语句的使用,
Condition替代了 Object 监视器方法的使用。
条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像
Object.wait做的那样。
Condition实例实质上被绑定到一个锁上。要为特定
Lock实例获得
Condition实例,请使用其
newCondition()方法。
作为一个示例,假定有一个绑定的缓冲区,它支持
put和
take方法。如果试图在空的缓冲区上执行
take操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行
put操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存
put线程和
take线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个
Condition实例来做到这一点。
class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
(
ArrayBlockingQueue类提供了这项功能,因此没有理由去实现这个示例类。)
Condition实现可以提供不同于
Object监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。
注意,
Condition实例只是一些普通的对象,它们自身可以用作
synchronized语句中的目标,并且可以调用自己的
wait和
notification监视器方法。获取
Condition实例的监视器锁或者使用其监视器方法,与获取和该
Condition相关的
Lock或使用其
waiting和
signalling方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用
Condition实例。
除非另行说明,否则为任何参数传递
null值将导致抛出
NullPointerException。
实现注意事项
在等待Condition时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为
Condition应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
三种形式的条件等待(可中断、不可中断和超时)在一些平台上的实现以及它们的性能特征可能会有所不同。尤其是它可能很难提供这些特性和维护特定语义,比如排序保证。更进一步地说,中断线程实际挂起的能力在所有平台上并不是总是可行的。
因此,并不要求某个实现为所有三种形式的等待定义完全相同的保证或语义,也不要求其支持中断线程的实际挂起。
要求实现清楚地记录每个等待方法提供的语义和保证,在某个实现不支持中断线程的挂起时,它必须遵从此接口中定义的中断语义。
由于中断通常意味着取消,而又通常很少进行中断检查,因此实现可以先于普通方法的返回来对中断进行响应。即使出现在另一个操作后的中断可能会释放线程锁时也是如此。实现应记录此行为。
*32-多线程(wait和sleep的区别)
wait 和 sleep 区别?
1,wait可以指定时间也可以不指定。
sleep必须指定时间。
2,在同步中时,对cpu的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
谁拿锁,谁执行。
*33-多线程(停止线程方式-定义标记)
停止线程的方法1. stop方法。
那怎么停下来呢?
第二种方法2. run方法结束。
停止线程:
1,stop方法。
2,run方法结束。
怎么控制线程的任务结束呢?
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常就用定义标记来完成。
class StopThread implements Runnable { private boolean flag = true; public void run() { while(flag) { System.out.println(Thread.currentThread().getName()+"....."+e); flag = false; } } public void setFlag() { flag = false; } } class StopThreadDemo { public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.setDaemon(true); t2.start(); int num = 1; for(;;) { if(++num==50) { st.setFlag(); break; } System.out.println("main...."+num); } System.out.println("over"); } }
*34-多线程(停止线程方式-Interrupt)
但是用标记停止线程不是最好的。因为存在一种情况,他不能使得线程停下来。
下面的例子:
class StopThread implements Runnable { private boolean flag = true; public synchronized void run() { while(flag) { try { wait();//t0 t1 } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+"....."+e); } System.out.println(Thread.currentThread().getName()+"......++++"); } } public void setFlag() { flag = false; } } class StopThreadDemo { public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.start(); int num = 1; for(;;) { if(++num==50) { <span style="white-space:pre"> </span> st.setFlag(); break; } System.out.println("main...."+num); } System.out.println("over"); } }
但是如果线程处于了冻结状态,无法读取标记。如何结束呢?
class StopThread implements Runnable { private boolean flag = true; public synchronized void run() { while(flag) { try { wait();//t0 t1 } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+"....."+e); flag = false; } System.out.println(Thread.currentThread().getName()+"......++++"); } } public void setFlag() { flag = false; } } class StopThreadDemo { public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.start(); int num = 1; for(;;) { if(++num==50) { // st.setFlag(); t1.interrupt(); t2.interrupt(); break; } System.out.println("main...."+num); } System.out.println("over"); } }
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。
当时强制动作会发生了InterruptedException,记得要处理。
*35-多线程(守护线程-setDaemon)
class StopThread implements Runnable { private boolean flag = true; public synchronized void run() { while(flag) { try { wait();//t0 t1 } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName()+"....."+e); flag = false; } System.out.println(Thread.currentThread().getName()+"......++++"); } } public void setFlag() { flag = false; } } class StopThreadDemo { public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.start(); t2.setDaemon(true); t2.start(); int num = 1; for(;;) { if(++num==50) { // st.setFlag(); t1.interrupt(); // t2.interrupt(); break; } System.out.println("main...."+num); } System.out.println("over"); } }当前台线程结束,后台线程也随之结束。
*36-多线程(其他方法-join等)
join用在哪?当我们在进行一个线程运算的时候,我们希望临时加进一个的时候,就要用join。
其他方法:
在讲线程组的概念:
另一个方法:
释放执行权。
*37-多线程(面试题)
/* new Thread() { public void run() { for(int x=0; x<50; x++) { System.out.println(Thread.currentThread().getName()+"....x="+x); } } }.start(); for(int x=0; x<50; x++) { System.out.println(Thread.currentThread().getName()+"....y="+x); } Runnable r = new Runnable() { public void run() { for(int x=0; x<50; x++) { System.out.println(Thread.currentThread().getName()+"....z="+x); } } }; new Thread(r).start(); */
出一个面试题:
第一题:
/*class Test implements Runnable
{
public void run(Thread t)
{}
}*/
//如果错误 错误发生在哪一行?错误在第一行,应该被abstract修饰
第二题:
相关文章推荐
- JavaSe基础XX15——线程_1
- JavaSe基础XX15——线程_2
- 黑马程序员_JavaSE基础15 之 常用对象API String类 SringBuffer类
- JavaSe基础XX01——函数
- JavaSe基础XX16——API对象-基本数据类型对象包装类
- JavaSe基础XX16——API对象-StringBuffer类
- 黑马程序员_JavaSE基础13 之 线程创建 线程安全 同步代码块
- JavaSe基础XX17——常用对象API-集合框架_4
- JavaSe基础XX16——Eclipse使用
- JavaSe基础XX14——包
- JavaSe基础XX17——常用对象API-集合框架_2
- JavaSe基础XX02——数组
- JavaSe基础XX07——面向对象
- JavaSe基础XX06——面向对象
- JavaSe基础XX14——习题解答_2
- JavaSe基础XX12——面向对象——[异常_1]
- JavaSE 拾遗(15)——JavaSE 高新技术基础增强...java5简单新特性和枚举
- JavaSe基础XX05——面向对象
- JavaSe基础XX09——面向对象
- JavaSe基础XX16——API对象-String