Java基础——线程间的通信+生产者消费者程序+守护线程+Join方法+线程优先级


1.   线程间通信-示例代码(不安全)

安全问题原因分析图

请看如下案例:

<span style="font-size:14px;">/*

线程间通讯:

其实就是多个线程在操作同一个资源,

但是操作的动作不同。

*/

class Res

{

         String name;

         String sex;}

 

class Input implements Runnable

{

         /*要想input,output操作的是同一个对象,可以用单例设计模式,Static生命长,不建议使用

         最好的办法是建立引用,初始化,不建立对象,调用的时候传进对象 r

         */

         private Res r ;

         Input(Res r)

         {

                   this.r = r;

         }

         public void run()

         {

                   int x = 0;

                   while(true)

                   {

                                     if(x==0)

                                     {

                                               r.name="mike";

                                               r.sex="man";

                                     }

                                     else

                                     {

                                               r.name="丽丽";

                                               r.sex = "女女女女女";

                                     }

                                     x = (x+1)%2;//目的是轮流写入mike和丽丽

                   }

         }

}

 

class Output implements Runnable

{

         /*要想input,output操作的是同一个对象,可以用单例设计模式,Static生命长,不建议使用

         最好的办法是建立引用,初始化,不建立对象,调用的时候传进对象 r

         */

         private Res r ;

         

         Output(Res r)

         {

                   this.r = r;

         }

         public void run()

         {

                   while(true)

                   {

                                     System.out.println(r.name+"...."+r.sex);

                   }

         }

         }

}

 

 

class  InputOutputDemo

{

         public static void main(String[] args) 

         {

                   Res r = new Res();

 

                   Input in = new Input(r);

                   Output out = new Output(r);

 

                   Thread t1 = new Thread(in);

                   Thread t2 = new Thread(out);

 

                   t1.start();

                   t2.start();

         }

}

</span>


 

2.   线程间通信-解决安全问题

当加了同步还是出错,就要考虑是否满足同步的前提了。

 

此例子中,要想使用同一个锁,代码块的锁(obj)不是同一个锁,同步函数的锁(this)也不可以,

类文件.class对象时唯一的,可以,但是牵强

最好是:共享资源对象 r

 

请看如下案例:

class Res

{

         String name;

         String sex;

}

 

class Input implements Runnable

{

         /*要想input,output操作的是同一个对象,可以用单例设计模式,Static生命长,不建议使用

         最好的办法是建立引用,初始化,不建立对象,调用的时候传进对象 r

         */

         private Res r ;

         Input(Res r)

         {

                   this.r = r;

         }

         public void run()//函数里面有些不需要同步所以不能用同步函数,成了单线程

         {

                   int x = 0;

                   while(true)

                   {

                            synchronized(r)

                            {

 

                                     if(x==0)

                                     {

                                               r.name="mike";

                                               r.sex="man";

                                     }

                                     else

                                     {

                                               r.name="丽丽";

                                               r.sex = "女女女女女";

                                     }

                                     x = (x+1)%2;

                            }

                   }

         }

}

 

class Output implements Runnable

{

         private Res r ;

         

         Output(Res r)

         {

                   this.r = r;

         }

         public void run()

         {

                   while(true)

                   {

                            synchronized(r)

                            {

                                      System.out.println(r.name+"...."+r.sex);

                            }

                   }

         }

}

 

 

class  InputOutputDemo

{

         public static void main(String[] args) 

         {

                   Res r = new Res();//唯一对象,用作锁

 

                   Input in = new Input(r);

                   Output out = new Output(r);

 

                   Thread t1 = new Thread(in);

                   Thread t2 = new Thread(out);

 

                   t1.start();

                   t2.start();

         }

}

3.   线程间通信-等待唤醒机制

问题:连续的input,连续的output

解决方法:利用等待唤醒机制,限制线程的执行资格,执行权

 

wait()notify(),notifyAll();方法等都使用在同步中,因为要对持有监视器()的线程操作。

所以要使用在同步中,因为只有同步才具有锁。

 

为什么这些操作线程的方法要定义Object类中呢?

因为这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,

只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。

不可以对不同锁中的线程进行唤醒。

也就是说,等待和唤醒必须是同一个锁。

而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

 如:

class Res

{

         String name;

         String sex;

         boolean flag = false;

}

 

class Input implements Runnable

{

 

         线程运行时会创建线程池,wait的线程在线程池中,notify()唤醒的是线程池中的线程,

         多个等待线程,唤醒的是第一个等待的线程;

 

         */

         private Res r ;

         Input(Res r)

         {

                   this.r = r;

         }

         public void run()

         {

                   int x = 0;

                   while(true)

                   {

                            synchronized(r)

                            {

 

                                     if(r.flag)//返回true才执行下一条语句

 

                                               try{r.wait();}catch(Exception e){}

                                               //wait方法声明异常,但是run方法是接口方法,不能声明,所以直接try处理。

                                               //r.wait(): wait()的是持有r这个锁的线程

 

                                     if(x==0)

                                     {

                                               r.name="mike";

                                               r.sex="man";

                                     }

                                     else

                                     {

                                               r.name="丽丽";

                                               r.sex = "女女女女女";

                                     }

                                     x = (x+1)%2;

                                     r.flag = true;

                                     r.notify();//input在wait前,唤醒output

                            }

                   }

         }

}

 

class Output implements Runnable

{

         private Res r ;

         

         Output(Res r)

         {

                   this.r = r;

         }

         public void run()

         {

                   while(true)

                   {

                            synchronized(r)

                            {

                                     if(!r.flag)

                                               try{r.wait();}catch(Exception e){}

                                     System.out.println(r.name+"...."+r.sex);

                                     r.flag = false;

                                     r.notify();

                            }

                   }

         }

}

 

 

class  InputOutputDemo

{

         public static void main(String[] args) 

         {

                   Res r = new Res();

 

                   Input in = new Input(r);

                   Output out = new Output(r);

 

                   Thread t1 = new Thread(in);

                   Thread t2 = new Thread(out);

 

                   t1.start();

                   t2.start();

         }

}

4.   线程间通信-代码优化

/*

线程间通讯:

其实就是多个线程在操作同一个资源,

但是操作的动作不同。

*/

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();

                   /*

                   Input in = new Input(r);

                   Output out = new Output(r);

 

                   Thread t1 = new Thread(in);

                   Thread t2 = new Thread(out);

 

                  t1.start();

                   t2.start();

                   */

         }

}


 

5.   线程间通信-生产者消费者(多线程处理)

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();

 

         }

}

 

/*

现象:多线程程序(超过两个线程)用if判断,notify()唤醒,出现了生产一次消费两次的情况;

 

对于多个生产者和消费者。

为什么要定义while判断标记。

原因:让被唤醒的线程再一次判断flag标记。

                            不用while,用if唤醒的线程不会再次判断了。

但是会全部wait,冻结。所以用notifyall()方法唤醒全部;

 

为什么定义notifyAll,

因为需要唤醒对方线程。

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

 

遗留问题:

把本方不需要的线程也唤醒了

*/

 

 

class Resource

{

         private String name;

         private int count = 1;

         private boolean flag = false;

                            // 生产者: t1    t2

         public synchronized void set(String name)

         {

                   //if(flag)只判断一次,线程唤醒后不判断flag

                   while(flag)//用循环,当线程唤醒后会再次判断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();//notify()方法只唤醒一个线程,

         }

 

 

         //  t3   t4  

         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();

                   }

         }

}

6.   线程间通信-生产者消费者JDK5.0升级版

常规开发思想

import java.util.concurrent.locks.*;

 

class ProducerConsumerDemo2 

{

         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();

 

         }

}

 

/*

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

将同步Synchronized(隐藏)替换成显示Lock操作。

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

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

该示例中,实现了本方只唤醒对方操作,不会唤醒本方。

======================================================

1.4版本一个锁对应一个wait,notify,要多个notify...就要重建同步,形成嵌套,容易死锁;

1.5以上,一个锁有好几组wait()等,对应多个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 = false;

                            //  t1    t2

         private Lock lock = new ReentrantLock();

         //一个锁可以有多个相关的condition

         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();//释放锁的动作一定要执行。

                   }

         }

 

 

         //  t3   t4  

         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();

                   }

                   

         }

}

 

class Producer implements Runnable

{

         private Resource res;

 

         Producer(Resource res)

         {

                   this.res = res;

         }

         public void run()

         {

                   while(true)

                   {

                            try

                            {

                                     res.set("+商品+");

                            }

                            catch (InterruptedException e)

                            {

                            }

                            

                   }

         }

}

 

class Consumer implements Runnable

{

         private Resource res;

 

         Consumer(Resource res)

         {

                   this.res = res;

         }

         public void run()

         {

                   while(true)

                   {

                            try

                            {

                                     res.out();

                            }

                            catch (InterruptedException e)

                            {

                            }

                   }

         }

}

7.   停止线程-中断wait()

如何停止线程?

stop方法已经过时。

只有一种,run方法结束。

开启多线程运行,运行代码通常是循环结构。

 

只要控制住循环,就可以让run方法结束,也就是线程结束。

 

特殊情况:

当线程处于了冻结状态。

就不会读取到标记。那么线程就不会结束。

 

interrupt()中断,即挂起:指强制将冻结状态清除,让你恢复到运行状态

//相当于催眠师把你催眠,别人一砖头把你拍醒,可是你受伤(中断异常)了。

sleep(),wait()等都能中断。

 

当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。

强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。

 

Thread类提供该方法 interrupt();

请看如下案例:

class StopThread implements Runnable

{

         private boolean flag =true;

         public  void run()

         {

                   while(flag)

                   {

                            try

                            {

                                     wait();//t1,t2线程都冻结,只执行主线程。当使用interrupt()方法时,就会捕捉异常。

                            }

                            catch (InterruptedException e)

                            {

                                     System.out.println(Thread.currentThread().getName()+"....Exception");

                                     flag=false;

                            }

                            System.out.println(Thread.currentThread().getName()+"....run");

                   }

         }

         public void changeFlag()

         {

                   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 = 0;

 

                   while(true)

                   {

                            if(num++ == 60)

                            {

                                     //st.changeFlag();

                                     t1.interrupt();//清除冻结后,设置flag=false;

                                     t2.interrupt();

                                     break;

                            }

                            System.out.println(Thread.currentThread().getName()+"......."+num);

                   }

                   System.out.println("over");

         }

}

8.  守护线程- setDaemon(true)

Java中有两类线程:User Thread(用户线程)Daemon Thread(守护线程)

 

用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:

前台线程全停了,后台线程(守护线程)才自动停止,接着jvm退出;(圣斗士是守护线程,雅典娜是前台线程)

 

只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。

Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。

 

UserDaemon两者几乎没有区别:

唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

 

请看如下案例:

class StopThread implements Runnable

{

         private boolean flag =true;

         public  void run()

         {

                   while(flag)

                   {

                            

                            System.out.println(Thread.currentThread().getName()+"....run");

                   }

         }

         public void changeFlag()

         {

                   flag = false;

         }

}

 

class  StopThreadDemo

{

         public static void main(String[] args) 

         {

                   StopThread st = new StopThread();

                   

                   Thread t1 = new Thread(st);

                   Thread t2 = new Thread(st);

 

                   //main是主线程

                   t1.setDaemon(true);//守护线程

                   t2.setDaemon(true);

                   t1.start();

                   t2.start();

 

                   int num = 0;

 

                   while(true)

                   {

                            if(num++ == 60)

                            {

                                     //st.changeFlag();

                                     //t1.interrupt();

                                     //t2.interrupt();

                                     break;

                            }

                            System.out.println(Thread.currentThread().getName()+"......."+num);

                   }

                   System.out.println("over");

         }

}

9.   Join()方法

join:

A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。

join()可以用来临时加入线程执行。

请看如下案例

class Demo implements Runnable

{

         public void run()

         {

                   for(int x=0; x<70; x++)

                   {

                            System.out.println(Thread.currentThread().toString()+"....."+x);

                   }

         }

}

 

class  JoinDemo

{

         public static void main(String[] args) throws Exception

         {

                   Demo d = new Demo();

                   Thread t1 = new Thread(d);

                   Thread t2 = new Thread(d);

                   t1.start();

                   

                   t2.start();

 

                   t1.join();//主线程冻结,只等join()的线程t1运行完才执行,不管t2线程。

 

                   for(int x=0; x<80; x++)

                   {

                            System.out.println("main....."+x);

                   }

                   System.out.println("over");

         }

}

10.  优先级&yield方法

线程的优先级:线程抢资源的频率

三级:MAX_PRIORITY--10

                           MIN_PRIORITY--5默认

                           NORM_PRIORITY--1

 

yield:

暂停当前正在执行的线程对象,并执行其他线程;

稍微减缓某一线程的运行次数,达到线程类似平均运行的效果

 请看如下案例:

class Demo implements Runnable

{

         public void run()

         {

                   for(int x=0; x<70; x++)

                   {

                            System.out.println(Thread.currentThread().toString()+"....."+x);

                            Thread.yield();

                   }

         }

}

 

class  JoinDemo

{

         public static void main(String[] args) throws Exception

         {

                   Demo d = new Demo();

                   Thread t1 = new Thread(d);

                   Thread t2 = new Thread(d);

                   t1.start();

                   

                   //t1.setPriority(Thread.MAX_PRIORITY);

 

                   t2.start();

 

                   //t1.join();//主线程只等join()的线程

 

                   for(int x=0; x<80; x++)

                   {

                            //System.out.println("main....."+x);

                   }

                   System.out.println("over");

         }

}

11.  开发程序中写线程

什么时候使用线程?

当某些代码需要同时被执行时,就用单独的线程封装;

可以封装出几个类(较麻烦),或是几个匿名Thread对象,或是Runnable对象;

 如

class ThreadTest 

{

         public static void main(String[] args) 

         {

                   

                   new Thread()

                   {

                            public void run()

                            {

                                     for(int x=0; x<100; x++)

                                     {

                                               System.out.println(Thread.currentThread().getName()+"....."+x);

                                     }

                            }

                   }.start();

                   

 

 

                   for(int x=0; x<100; x++)

                   {

                            System.out.println(Thread.currentThread().getName()+"....."+x);

                   }

 

                   Runnable r  = new Runnable()

                   {

                            public void run()

                            {

                                     for(int x=0; x<100; x++)

                                     {

                                               System.out.println(Thread.currentThread().getName()+"....."+x);

                                     }

                            }

                   };

                   new Thread(r).start();

 

 

                   //new Test1().start();

         }

}

/*

class Test1 extends Thread

{

         public void run()

         {

                   for(int x=0; x<100; x++)

                   {

                            System.out.println(Thread.currentThread().getName()+"....."+x);

                   }

         }

}

*/


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值