黑马程序员——java基础---多线程(二)

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

    线程间的通信:简单来说,就是多个线程在操作同一资源,但操作的动作不同。

  试想一下,对于同一个资源做不同的操作,这势必会在操作的过程中产生矛盾。为了避免这种情况的发生,就需要用的synchronized来保证,每次对共享资源的操作,只能是一条线程在进行。在用到同步的时候,就会因需求问题用到wait()、notify()、notifyAll()这三个方法。

  wait()方法:作用是使调用线程自动释放CPU使用权,从而使线程本身进入线程池继续等待。

  notify()方法:由运行的线程唤醒线程池中排在最前面的等待线程,使该线程具有使用CPU的资格。

  notifyAll()方法:由运行的线程唤醒线程池中的所有等待线程。

  因为三个方法都需以对持有(锁)监视器的对象操作,也就使他们只能在同步中使用,因为只有同步中才会出现锁这个概念。而通过查阅API,我们很容易就发现这三个方法都被定义在Object类当中,那么这些操作锁的方法为什么会被定义在Object类中呢?我们可以这样理解,同步中对于锁的标志位可以是任意的特有对象,而这三个方法都是操作锁的,也就是说他们可以操作任何对象,Java中能操作任何对象的类也只有Object类了。

  虽然有这些方法来使同步机制更加完善,但是对于某些情况同步机制并不能完美的完成相应的功能。

  针对以下代码:

 1 *
 2  * 生产者消费者问题
 3  */
 4 public class ProducerConsumerDemo{
 5     public static void main(String[] args){
 6         Resource r = new Resource();
 7         Producer pro = new Producer(r);
 8         Consumer con = new Consumer(r);
 9
10         Thread t1 = new Thread(pro);
11         Thread t3 = new Thread(pro);
12         Thread t2 = new Thread(con);
13         Thread t4 = new Thread(con);
14
15         t1.start();
16         t2.start();
17         t3.start();
18         t4.start();
19     }
20 }
21 //定义共享的资源类
22 class Resource{
23     private String name;
24     private int count = 1;
25     private boolean flag = false;
26
27     public synchronized void set(String name){
28         while(flag)
29             try{
30                 this.wait();
31             }catch(Exception e){
32
33             }
34         this.name = name+"--"+count++;
35         System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
36         flag = true;
37         this.notifyAll();
38     }
39     public synchronized void out(){
40         while(!flag)
41             try{
42                 wait();
43             }catch(Exception e){
44             }
45         System.out.println(Thread.currentThread().getName()+"-----消费者-----"+this.name);
46         flag = false;
47         this.notifyAll();
48     }
49 }
50 //定义生产者类
51 class Producer implements Runnable{
52     private Resource res;
53     Producer(Resource res){
54         this.res = res;
55     }
56     public void run(){
57         while(true){
58             res.set("+商品+");
59         }
60     }
61 }
62 //定义消费者类
63 class Consumer implements Runnable{
64     private Resource res;
65     Consumer(Resource res){
66         this.res = res;
67     }
68     public void run(){
69         while(true){
70             res.out();
71         }
72     }
73 }

  在这个类中虽然可以初步满足生产者消费者这一功能需求,但我们可以发现在代码中我们用了notifyAll()这个方法,这个方法每运行一次将会把所有的等待状态的线程全部唤醒,这会给计算机带来很大的耗费,这种情况显然并不是我们所期望的。

   针对上述问题,官方在JDK1.5版本中进行了升级:

  * 引进了Lock接口从而替代了synchronized
  * 用Condition 替代了 Object监视器方法
  这样做的好处就是:一定程度上简化了同步代码的编写;一个Condition对象可以对不同锁进行操作,而且可以根据需求对特定的线程进行唤醒。而同时也要注意:一定要在对应的程序代码尾部执行释放锁的操作。  下面是通过运用Lock和Condition改进后的代码:
 1 import java.util.concurrent.locks.Condition;
 2 import java.util.concurrent.locks.ReentrantLock;
 3
 4 /*
 5  * 生产者消费者问题
 6  */
 7 public class ProducerConsumerDemo2{
 8     public static void main(String[] args){
 9         Resource2 r = new Resource2();
10         Producer2 pro = new Producer2(r);
11         Consumer2 con = new Consumer2(r);
12
13         Thread t1 = new Thread(pro);
14         Thread t3 = new Thread(pro);
15         Thread t2 = new Thread(con);
16         Thread t4 = new Thread(con);
17
18         t1.start();
19         t2.start();
20         t3.start();
21         t4.start();
22     }
23 }
24 //定义共有的资源
25 class Resource2{
26      private String name;
27      private int count = 1;
28      private boolean flag = false;
29
30      private ReentrantLock lock = new ReentrantLock();
31      Condition c_pro = lock.newCondition();
32      Condition c_con = lock.newCondition();
33
34     public void set(String name) throws InterruptedException{
35         lock.lock();
36         try{
37             while(flag)
38                     c_pro.await();
39             this.name = name+"--"+count++;
40             System.out.println(Thread.currentThread().getName()+"---生产者---"+this.name);
41             flag = true;
42             c_con.signal();
43         }finally{
44             lock.unlock();
45         }
46     }
47     public void out() throws InterruptedException{
48         lock.lock();
49         try{
50             while(!flag)
51                 c_con.await();
52             System.out.println(Thread.currentThread().getName()+"-----消费者-----"+this.name);
53             flag = false;
54             c_pro.signal();
55         }finally{
56             lock.unlock();
57         }
58     }
59 }
60 //定义生产者
61 class Producer2 implements Runnable{
62     private Resource2 res;
63     Producer2(Resource2 r){
64         this.res = r;
65     }
66     public void run(){
67         while(true){
68             try {
69                 res.set("+商品+");
70             } catch (InterruptedException e) {
71                 // TODO Auto-generated catch block
72                 e.printStackTrace();
73             }
74         }
75     }
76 }
77 //定义消费者
78 class Consumer2 implements Runnable{
79     private Resource2 res;
80     Consumer2(Resource2 r){
81         this.res = r;
82     }
83     public void run(){
84         while(true){
85             try {
86                 res.out();
87             } catch (InterruptedException e) {
88                 e.printStackTrace();
89             }
90         }
91     }
92 }
 接下来是关于线程的停止:   1、正常的情况是,等待线程运行完,自动释放CPU占用权。这种方式也是最为安全的。     2、多线程中常用循环控制语句,我们可以通过设置标志位,来使线程在不满足条件的情况下自动释放CPU资源,停止运行。   3、对于占有资源但却因为某种原因进入冻结的线程,我们可以使用interrupt()方法来打断其状态,进而使之可以正常运行释放资源。

 设置守护进程的方法:通过调用SetDaemon()方法,可以把对应进程设置成守护进程或者是用户进程。

  join()方法:作用是长期占有CPU使用权。原理是:当A线程执行到了B线程的join方法时,A就会等待,等B线程执行完,A才会执行。Join可以用来临时加入线程执行。   代码示例:
 1 class Demo implements Runnable{
 2     public void run(){
 3         for(int x=0; x<20; x++){
 4             System.out.println(Thread.currentThread().getName()+"---"+x);
 5         }
 6     }
 7 }
 8 public class JoinTest {
 9     public static void main(String[] args) throws InterruptedException {
10         Demo d = new Demo();
11         Thread t1 = new Thread(d);
12         Thread t2 = new Thread(d);
13
14         t1.start();
15         t1.join();
16         t2.start();
17
18         for(int x=0; x<30; x++){
19             System.out.println("main--"+x);
20         }
21         System.out.println("over");
22     }
23 }
  执行结果:
 1 Thread-0---0
 2 Thread-0---1
 3 Thread-0---2
 4 Thread-0---3
 5 Thread-0---4
 6 Thread-0---5
 7 Thread-0---6
 8 Thread-0---7
 9 Thread-0---8
10 Thread-0---9
11 Thread-0---10
12 Thread-0---11
13 Thread-0---12
14 Thread-0---13
15 Thread-0---14
16 Thread-0---15
17 Thread-0---16
18 Thread-0---17
19 Thread-0---18
20 Thread-0---19
21 main--0
22 Thread-1---0
23 main--1
24 Thread-1---1
25 Thread-1---2
26 Thread-1---3
27 Thread-1---4
28 Thread-1---5
29 Thread-1---6
30 Thread-1---7
31 main--2
32 Thread-1---8
33 Thread-1---9
34 main--3
35 Thread-1---10
36 main--4
37 Thread-1---11
38 Thread-1---12
39 Thread-1---13
40 Thread-1---14
41 Thread-1---15
42 Thread-1---16
43 main--5
44 main--6
45 main--7
46 main--8
47 main--9
48 main--10
49 main--11
50 main--12
51 main--13
52 main--14
53 main--15
54 main--16
55 main--17
56 Thread-1---17
57 main--18
58 Thread-1---18
59 main--19
60 Thread-1---19
61 main--20
62 main--21
63 main--22
64 main--23
65 main--24
66 main--25
67 main--26
68 main--27
69 main--28
70 main--29
71 over

  我们可以发现只有当线程t1执行完以后,main线程才会和t2线程继续强度CPU的使用权,进而相继输出。所以可以理解为join()方法是线程具有了长期占有CPU的资格。

    关于线程优先级的知识

     1、线程的优先级设置范围为(1-10);

       2、线程默认的优先级是5;

3、我们可以通过SetPriority(1-10)来对线程的优先级进行设置。

     4、因为相邻的优先级效果差别很小,说以一般提倡对优先级设置一下几个值:Thread.MAX_PRIORITY 10 ;Thread.MIN_PRIORITY 1;Thread.NORM_PRIORITY 5
   yield()方法:暂停当前正在执行的线程,转而去执行其它线程。 
开发中应用:保证以下三个代码同时运行。
案例代码:
 1 new Thread(){
 2       for(int x=0;x<100;x++){
 3          sop(Thread.currentThread().getName())
 4       }
 5 }.start();
 6
 7       for(int x=0;x<100;x++){
 8          sop(Thread.currentThread().getName())
 9       }
10
11 Runnable r=new Runnable(){
12    public voud run(){
13       for(int x=0;x<100;x++){
14          sop(Thread.currentThread().getName())
15       }
16    }
17 };
18
19 new Thread(r).start();
				
时间: 2024-12-17 17:47:43

黑马程序员——java基础---多线程(二)的相关文章

黑马程序员——java基础——多线程

 黑马程序员--java基础--多线程 ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 进程:是一个正在执行中的程序.每一个进程执行都有一个执行顺序.该顺序是一个执行路径,或者叫一个控制单元. 线程:就是进程中的一个独立的控制单元.线程在控制着进程的执行.一个进程中至少有一个线程. 一个进程至少有一个线程在运行,当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序,每个线程在栈区中都有自己的执行空间,自己的方法区.自己的变量.

黑马程序员——Java基础---IO(二)---IO字节流、流操作规律

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! -------   字节流 一.概述 1.字节流和字符流的基本操作是相同的,但字节流还可以操作其他媒体文件. 2.由于媒体文件数据中都是以字节存储的,所以,字节流对象可直接对媒体文件的数据写入到文件中,而可以不用再进行刷流动作. 3.读写字节流:Inpu

黑马程序员——Java基础语法(二)---流程控制

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 本文主要介绍java基础语法中的流程控制,流程控制是我们写出灵活代码的基础.常见的流程控制主要有四类:判断结构.选择结构.循环结构.其他控制结构 一.判断结构 判断结构的标志是if语句,if语句主要有三种表现形式: 1.if(条件表达式) { 执行语句 } 注意: 1.如果if语句中只有一条语句,那么可以不写大括号.不过初学者一定要写括号,以免出错. 2.如果if语句没写大括号,if就只能控制

黑马程序员--Java基础--多线程|线程同步

--Java培训.Android培训.iOS培训..Net培训 期待与您共同交流!-- 多线程基础.线程同步 1. 多线程基础 1.1. 进程和线程 1.1.1. 什么是进程 所谓进程(process)就是一块包含了某些资源的内存区域.操作系统利用进程把它的工作划分为一些功能单元.进程中所包含的一个或多个执行单元称为线程(thread).进程还拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问.线程只能归属于一个进程并且它只能访问该进程所拥有的资源.当操作系统创建一个进程后,该进程会自动

黑马程序员-java基础-多线程2

5.多线程的安全问题:多线程同步 当使用多个线程同时访问一个数据时,经常会出现线程安全问题.如下面程序: 1 package Thread; 2 3 /* 4 * 多个线程同时访问一个数据时,出现的安全问题. 5 * 模拟一个卖火车票系统:一共有100张票,多个窗口同时卖票 6 */ 7 class Ticks implements Runnable 8 { 9 private int ticks = 100 ; 10 public void run() 11 { 12 while (ticks

黑马程序员——java基础---多线程

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 学习多线程之前,需对以下几个概念有所认知: 进程:进程是动态的.是一个正在执行中的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元. 线程:线程依附于进程,可以理解为进程下的一个子执行路径,但没有进程线程无法单独执行. 两者间的区别:进程是重量级的计算机任务,需要给它分配独立的地址空间和系统资源等.不同进程的内部数据和状态都是完全独立,所以不同进程之间的通信或转换

黑马程序员-java基础-多线程1

---恢复内容开始--- 单线程的程序只有一个顺序流:而多线程的程序则可以包括多个顺序执行流,并且多个顺序流之间互不干扰.就像单线程程序如同只雇佣了一个服务员的餐厅,他只有做完一件事情后才可以做下面一件事情:而多线程程序则是雇佣了多名服务员的餐厅,他们可以同时进行着多件事情. JAVA多线程编程的相关知识:创建.启动线程.控制线程.以及多线程的同步操作. 1.概述: 进程是指正在运行中的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或叫一个执行单元. 线程是指进程中能够独立执行的控

黑马程序员-Java基础-多线程

第一讲  多线程概述 1. 定义 进程:是一个正在执行中的程序.每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元.在程序运行时,会被分配一个内存空间,进程就用于标识这个空间,封装单元,线程才是线程中真正执行的哦部分. 线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行. 一个进程中至少有一个线程. 例子:java JVM 启动时会有一个进程java.exe.该进程中至少一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程就称为主线程

黑马程序员-java基础-IO(二)

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------ 一.概述 Java除了基本的字节流.字符流之外,还提供了File类.properties类.打印流.序列流等和输入输出相关的类,它们能够帮助我们更好的处理信息.下面将对它们进行简单的介绍. 一.正文 1.File类       File类是对文件系统中文件以及文件夹进行封装的对象,可以通过对象的思想来操作文件和文件夹.它的特点是:用来将文件或文件夹封装成对象:方便于对文件与文件夹的属性信息进行操