黑马程序员--多线程2

------- android培训java培训、期待与您交流! ----------

一、线程间的通讯

线程间通讯:其实就是多个线程在操作同一个资源,但是操作的动作不同。
wait():   notify();   notifyAll();
都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。
 
为什么这些操作线程的方法要定义Object类中呢?
因为这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。不可以对不同锁中的线程进行唤醒。
也就是说,等待和唤醒必须是同一个锁。
而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

class Res { private String name; private String sex; private boolean flag = false; public synchronized void set(String name,String sex) { if(flag) try{this.wait();}catch(Exception e){} this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void out() { if(!flag) try{this.wait();}catch(Exception e){} System.out.println(name+"........"+sex); flag = false; this.notify(); } } class Input implements Runnable { private Res r ; Input(Res r) { this.r = r; } public void run() { int x = 0; while(true) { if(x==0) r.set("mike","man"); else r.set("丽丽","女女女女女"); x = (x+1)%2; } } } class Output implements Runnable { private Res r ; Output(Res r) { this.r = r; } public void run() { while(true) { r.out(); } } } class InputOutputDemo2 { public static void main(String[] args) { Res r = new Res(); new Thread(new Input(r)).start(); new Thread(new Output(r)).start(); } }

二、生产者与消费者

当生产者在生产时,消费者等待。生产者生产完毕,唤醒消费者,消费者消费。消费者消费时,生产者等待,消费者消费完毕,唤醒生产者生产。实现代码如下:

class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t1 = new Thread(pro); Thread t2 = new Thread(pro); Thread t3 = new Thread(con); Thread t4 = new Thread(con); t1.start(); t2.start(); t3.start(); t4.start(); } } class Resource { private String name; private int count = 1; private boolean flag = false; public synchronized void set(String name) { while(flag) try{this.wait();}catch(Exception e){}//t1(放弃资格) t2(获取资格) this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag = true; this.notifyAll(); } public synchronized void out() { while(!flag) try{wait();}catch(Exception e){}//t3(放弃资格) t4(放弃资格) System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name); flag = false; this.notifyAll(); } } class Producer implements Runnable { private Resource res; Producer(Resource res) { this.res = res; } public void run() { while(true) { res.set("+商品+"); } } } class Consumer implements Runnable { private Resource res; Consumer(Resource res) { this.res = res; } public void run() { while(true) { res.out(); } } }

对于多个生产者和消费者。
为什么要定义while判断标记。
原因:让被唤醒的线程再一次判断标记。

为什么定义notifyAll:

因为需要唤醒对方线程。因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。

三、同步问题的升级解决方案

JDK1.5 中提供了多线程升级解决方案。

将同步Synchronized替换成显示的Lock操作。

将Object中的wait,notify notifyAll,替换成了Condition对象。

该对象可以Lock锁进行获取。

实现了本方只唤醒对方操作,因为一个锁可以相关多个Condition对象。

Lock:替代了Synchronized

  lock 

  unlock

  newCondition()

Condition:替代了Object wait notify notifyAll

  await();

  signal();

  signalAll();

核心代码:

class Resource { private String name; private int count = 1; private boolean flag = fals private Lock lock = new ReentrantLock(); private Condition condition_pro = lock.newCondition(); private Condition condition_con = lock.newCondition(); public void set(String name)throws InterruptedException { lock.lock(); try { while(flag) condition_pro.await();//t1,t2 this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag = true; condition_con.signal(); } finally { lock.unlock();//释放锁的动作一定要执行。 } } public void out() throws InterruptedException { lock.lock(); try { while(!flag) condition_con.await(); System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name); flag = false; condition_pro.signal(); } finally { lock.unlock(); } } }

四、停止线程
API中有stop方法,但是已经过时。
如何停止线程?
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。 只要控制住循环,就可以让run方法结束,也就是线程结束。

特殊情况:
当线程处于了冻结状态。 就不会读取到标记,那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。 强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法 interrupt();

interrupt

public void interrupt()
中断线程。

如果当前线程没有中断它自己(这在任何情况下都是允许的),则该线程的 checkAccess
方法就会被调用,这可能抛出 SecurityException

如果线程在调用 Object 类的 wait()wait(long) wait(long,
int)
方法,或者该类的 join()join(long)join(long,
int)
sleep(long) sleep(long,
int)
方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException

如果该线程在可中断的通道上的
I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException

如果该线程在一个 Selector
中受阻,则该线程的中断状态将被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用了选择器的 wakeup
方法一样。

如果以前的条件都没有保存,则该线程的中断状态将被设置。


中断一个不处于活动状态的线程不需要任何作用。


抛出:
SecurityException -
如果当前线程无法修改该线程

五、例题分析

class MyThread extends Thread{ public void run(){ try { Thread.currentThread().sleep(3000); } catch (InterruptedException e) { } System.out.println("MyThread running"); } } public class ThreadTest{ public static void main(String argv[]) { MyThread t = new MyThread(); t.run(); t.start(); System.out.println("Thread Test"); } }

分析:
代码分析过程:
MyThread t = new MyThread(); 创建了一个线程。

t.run();
 调用MyThread对象的run方法; 这时只有一个线程在运行就是主线程; 当主线程执行到了run方法中的sleep(3000);  时; 这时主线程处于冻结状态。程序并没有任何执行; 当3秒过后,主线程打印了  MyThread running, run方法执  行结束。
 
t.start(); 开启了t线程。
有两种可能情况。
第一种,主线程在只执行了t.start()后,还具有执行权,继续往下执行, 打印了Thread Test。主线程结束。 线程获   取执行权,调用自己的run方法。然后执行的sleep(3000);冻结3秒。 3秒后,打印MyThread running t线程结束,整   个程序结束。
 
第二种: 主线程执行到t.start();开启了t线程,t线程就直接获取到了执行权。 就调用自己的run方法。
 指定到sleep(3000).t线程冻结3秒,这是t线程就是释放了执行权。 那么主线程开始执行打印了Thread Test,主线程  结束。 等到3秒后,t线程打印MyThread running ,然后t线程结束。 程序结束。
六、Thread类中的其他几个方法
join:
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。join可以用来临时加入线程执行。

 voidjoin() 
          等待该线程终止。

 voidsetDaemon(boolean on) 
          将该线程标记为守护线程或用户线程

 voidsetName(String name) 
          改变线程名称,使之与参数 name 相同。

 voidsetPriority(int newPriority) 
          更改线程的优先级。

static voidyield() 
          暂停当前正在执行的线程对象,并执行其他线程。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
黑马程序员多线程练习题主要包括两个问题。第一个问题是如何控制四个线程在打印log之前能够同时开始等待1秒钟。一种解决思路是在线程的run方法中调用parseLog方法,并使用Thread.sleep方法让线程等待1秒钟。另一种解决思路是使用线程池,将线程数量固定为4个,并将每个调用parseLog方法的语句封装为一个Runnable对象,然后提交到线程池中。这样可以实现一秒钟打印4行日志,4秒钟打印16条日志的需求。 第二个问题是如何修改代码,使得几个线程调用TestDo.doSome(key, value)方法时,如果传递进去的key相等(equals比较为true),则这几个线程应互斥排队输出结果。一种解决方法是使用synchronized关键字来实现线程的互斥排队输出。通过给TestDo.doSome方法添加synchronized关键字,可以确保同一时间只有一个线程能够执行该方法,从而实现线程的互斥输出。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [黑马程序员——多线程10:多线程相关练习](https://blog.csdn.net/axr1985lazy/article/details/48186039)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值