Java基础:多线程

1.进程和线程

进程:正在执行中的程序;每一个进程执行都有一个执行顺序,该顺序是一个执行路径,又叫控制单元;
线程:线程是进程中的内容,进程中的一个独立的控制单元,线程控制着进程的执行;
Java虚拟机允许应用程序并发的运行多个执行线程;
当引进线程后,CPU在各个线程之间作着快速的切换,单核CPU在某一时刻只有一个线程在运行,多个线程在宏观上同时执行;
多线程与多进程:
(1).一个进程中至少有一个线程;
(2).与进程相比较,线程更“轻量级”,开销小;
(3).每个进程拥有自己的一整套变量,而线程则共享数据,共享数据使得线程之间的通信相比进程之间的通信更有效率,更容易,但是同时也会出现共享数据的安全问题;
主线程:
JVM在编译时期会生成一个javac.exe的进程,在运行时期,会生成一个java.exe的进程;
执行期间java.exe进程中至少有一个线程在负责Java程序的执行,而且这个线程运行的代码存在于Java程序的main()方法中,该线程称之为主线程;
JVM在启动时并不只是只执行一个主线程那么简单,也就是说并非是单线程,而是多线程的执行任务,最明显的就是在程序运行时期还会有专门负责垃圾回收的线程在执行;
多线程执行的意义所在:
(1).在宏观上产生一个同时运行的效果;
(2).提高执行效率;

2.线程的状态

线程的状态:
(1).BLOCKED:受阻塞的,正在等待监视器锁的线程;
(2).NEW:创建后尚未启动的线程;
(3).RUNNABLE:可运行的线程;
(4).WAITING:等待线程;
(5).TIMED_WAITING:具有指定等待时间的等待线程;
(6).TERMINATED:终止的线程;
线程的状态示意图:


3.线程的创建

3.1 继承方式

Java提供的类库中提供了对线程这类事物的描述:封装为Thread类;
创建线程的继承方式:将自定义的类声明为Thread类的子类,重写run()方法;
具体步骤:
(1).定义类继承Thread类;
Thread类中定义了一些用来获取和操作线程对象的方法:
获取当前正在执行的线程对象的引用:static Thread currentThread();
获取线程的名称:String getName();默认名称:Thread-编号(从0开始);
获取线程的优先级:int getPriority();
获取线程的状态:Thread.State getState();
设置线程名称:void setName(String name)或者在构造时调用父类的构造方法;
设置线程优先级:void setPriority(int newPriority);
获取线程信息:String toString();包括线程的名称,优先级,线程组;

(2).覆盖重写Thread类中的run()方法:public void run(){}

覆盖run()方法:将自定义的代码存储在run方法中,供线程运行;

1).Thread类用于描述线程,该类定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法;run方法相当于线程执行代码的一个存储空间;

2).主线程的执行代码存放在main方法中,自定义线程的执行代码存放在自定义线程的run方法中;

3).Thread类中的run方法没有定义有意义的内容,父类提供了功能定义,子类只需覆盖,添加自己的内容即可;

(3).创建自定义类的对象,即创建线程;

(4).调用类中从Thread继承来的start()方法:启动线程;执行run方法;

注意:

对象.run():仅仅就是普通的对象调用方法;线程创建了,但是没有启动运行;

对象.start():开启线程并执行线程中的run()方法;

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <span style="font-size:18px;">/*</span> 
  2.  * 使用继承方式创建线程; 
  3.  */  
  4.   
  5. package com.zr.day11;  
  6.   
  7. //继承Thread类  
  8. class MyThread extends Thread     
  9. {  
  10.     //构造方法  
  11.     MyThread(String name)  
  12.     {  
  13.         //在父类Thread中已经实现这样的构造方式,子类中只需使用super调用即可;  
  14.         super(name);  
  15.     }  
  16.       
  17.     //覆盖父类中的run()方法,在这个方法中是简单的打印输出;  
  18.     //自定义线程的代码存放在run()方法中;  
  19.     public void run()  
  20.     {  
  21.         for(int x=0; x<80; x++)  
  22.         {  
  23.             //调用sleep方法用来方法线程交替执行的效果  
  24.             try {  
  25.                 Thread.sleep(10);  
  26.             } catch (InterruptedException e) {  
  27.                 e.printStackTrace();  
  28.             }  
  29.             System.out.println(Thread.currentThread().getName()+"running !-------");  
  30.         }  
  31.     }  
  32. }  
  33.   
  34. class ThreadDemo  
  35. {  
  36.     //主线程执行的代码存放在main()方法中;  
  37.     public static void main(String[] args)  
  38.     {  
  39.         //创建自定义的类对象,相当于创建了一个新线程;  
  40.         MyThread my = new MyThread("my_thread");  
  41.         //线程启动,调用run()方法;  
  42.         my.start();  
  43.           
  44.         //同样在主线程中定义打印输出操作;  
  45.         for(int x=0; x<100; x++)  
  46.         {  
  47.             try {  
  48.                 Thread.sleep(10);  
  49.             } catch (InterruptedException e) {  
  50.                 e.printStackTrace();  
  51.             }  
  52.             System.out.println("main running");  
  53.         }  
  54.     }  
  55. }  

(5).对上述线程执行过程的分析:

程序执行后,有两个线程在运行(实际上不止两个),一个是主线程,一个是自己创建的线程;

多个线程都需要获取CPU执行权,但是在某一时刻,只能有一个线程在运行(多核CPU除外),CPU在各个线程之间做着频繁的切换,从宏观上是在同时运行;

通过运行结果的不确定性体现了多线程运行时的特性:随机性;


3.2 实现方式

通过对JavaAPI的分析,发现Thread也是实现了上层的一个接口:Runnable接口;

另外一种创建线程的方式就是实现Runnable接口;

具体步骤:

(1).自定义类实现Runnable接口;

(2).覆盖Runnable接口中的run()方法;

将自定义线程需要执行的代码存放在run方法中;

(3).通过Thread类建立线程对象;

(4).将Runnable接口的子类对象作为实际参数传递给Thread类的构造方法;

自定义的run方法所属的对象是Runnable接口的子类对象,要让new线程执行指定对象的run方法,就在线程创建时明确该run方法所属的对象;

(5).调用Thread类的start方法启动线程,并调用Runnable接口子类的run方法;

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 实现卖票过程; 
  3.  * 1.一个票务中心,主要提供票务,以及向各个窗口提供售票的程序; 
  4.  * 2.四个售票窗口; 
  5.  */  
  6.   
  7. package com.zr.day11;  
  8.   
  9. //自定义类实现Runnable接口  
  10. class TicketCenter implements Runnable  
  11. {  
  12.     //多个线程共享数据  
  13.     private int tick = 100;  
  14.     //覆盖run方法  
  15.     public void run()  
  16.     {  
  17.         while(true)  
  18.         {  
  19.             if(tick>0)  
  20.             {  
  21.                 //调用sleep方法来方法线程执行效果,同时将多个线程共享数据的安全问题暴露出来;  
  22.                 try {  
  23.                     Thread.sleep(10);  
  24.                 } catch (InterruptedException e) {  
  25.                     e.printStackTrace();  
  26.                 }  
  27.                 System.out.println(Thread.currentThread().getName()+":::卖出第"+(tick--)+"张票!");  
  28.             }  
  29.         }  
  30.     }  
  31. }  
  32. class ThreadTest  
  33. {  
  34.     public static void main(String[] args)  
  35.     {  
  36.         //创建Runnable接口的子类对象  
  37.         TicketCenter tc = new TicketCenter();  
  38.           
  39.         //创建线程,将Runnable接口的子类对象作为参数传递给线程的构造方法,同时可以设置线程名称;  
  40.         Thread t1 = new Thread(tc,"窗口一");  
  41.         Thread t2 = new Thread(tc,"窗口二");  
  42.         Thread t3 = new Thread(tc,"窗口三");  
  43.         Thread t4 = new Thread(tc,"窗口四");  
  44.         //启动各个线程  
  45.         t1.start();  
  46.         t2.start();  
  47.         t3.start();  
  48.         t4.start();  
  49.   
  50.     }  
  51. }  
运行结果:


3.3 实现方式和继承方式总结

实现方式:建议使用;避免了单继承的局限性;线程代码存放在Runnable接口子类的run方法中;
继承方式:当继承Thread类后,就不能在继承其他类;线程代码存放在Thread子类的run方法中;


4.多线程中的安全问题

(1).问题产生的原因

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,并没有执行完,而这时另一个线程也参与进来开始执行这部分代码,导致共享数据的错误;

(2).解决办法

对多条操作共享数据的语句在执行时,只能让一个线程执行完,在执行过程中,不允许其他线程参与执行;

(3).同步代码块

Java中对于多线程的安全问题提供的专业的解决方法;

格式:

synchronized(锁对象)    //持有锁的线程可以在同步中执行;

{

需要被同步的代码块(操作共享数据);

}

如果某个线程的整个run方法被同步了就变成了单线程;

没有持有锁的线程即使获得了CPU执行权,也不能运行,还是要等待;

(4).同步必须要满足的两个前提

必须要有两个和两个以上的线程;

必须是多个线程使用同一个锁;

(5).同步的利弊

利:解决多线程的安全问题;

弊:多个线程要判断锁,消耗系统资源;

(6).查找安全问题

1).明确哪些代码是多线程运行代码;

2).明确共享数据;

3).明确多线程运行代码中哪些语句是操作共享数据的;

(7).同步函数

当整个方法中的代码都需要被同步时,可以将该方法定义成同步函数,使用关键字synchronized修饰,关键字放在返回值类型前面;

同步函数的示例:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 银行存款:有两个账户向银行存款; 
  3.  * 同步函数实现; 
  4.  */  
  5.   
  6. package com.zr.day11;  
  7.   
  8. class Bank  
  9. {  
  10.     private int sum;  
  11.     //将add方法封装成同步函数;在函数中操作的sum是共享数据,在赋值操作和打印操作之间可能出现错误情况;  
  12.     public synchronized void add(int num)  
  13.     {  
  14.         sum =  sum + num;  
  15.         System.out.println(Thread.currentThread().getName()+"---银行总额:"+sum);  
  16.     }  
  17. }  
  18.   
  19.   
  20. //实现Runnable接口  
  21. class Counter implements Runnable  
  22. {  
  23.     private Bank b = new Bank();  
  24.     //两个账户分别向银行中存钱;没人存三次一次一百;  
  25.     public void run()  
  26.     {  
  27.         for(int x=0; x<3; x++)  
  28.         {  
  29.             try {  
  30.                 Thread.sleep(10);  
  31.             } catch (InterruptedException e) {  
  32.                 e.printStackTrace();  
  33.             }  
  34.             b.add(100);  
  35.         }  
  36.     }  
  37. }  
  38.   
  39. class ThreadBank  
  40. {  
  41.     public static void main(String[] args)  
  42.     {  
  43.         Counter c = new Counter();  
  44.         new Thread(c,"账户A").start();  
  45.         new Thread(c,"账户B").start();  
  46.     }  
  47. }  


同步函数的锁:函数需要被对象调用,函数都有一个所属的对象引用this,所以同步函数的锁是this;

验证同步函数的锁是this:

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 卖票程序:两个线程,一个线程在同步函数中执行,一个线程在同步代码块中执行; 
  3.  * 用来验证同步函数的锁是this; 
  4.  */  
  5.   
  6. package com.zr.day11;  
  7.   
  8. class TicketCenter implements Runnable  
  9. {  
  10.     private int tick = 100;  
  11.     boolean flag = true;  
  12.     //定义同步函数;  
  13.     public synchronized void show()  
  14.     {  
  15.         if(tick>0)  
  16.         {  
  17.             try {  
  18.                 Thread.sleep(10);  
  19.             } catch (InterruptedException e) {  
  20.                 e.printStackTrace();  
  21.             }  
  22.             //执行同步函数的内容;  
  23.             System.out.println(Thread.currentThread().getName()+":::------show卖出第"+(tick--)+"张票!");  
  24.         }  
  25.     }  
  26.     public void run()  
  27.     {  
  28.         if(flag)  
  29.         {  
  30.             while(true)  
  31.             {  
  32.                 //执行同步代码块中的内容;  
  33.                 synchronized(this)  
  34.                 {  
  35.                     if(tick>0)  
  36.                     {  
  37.                         try {  
  38.                             Thread.sleep(10);  
  39.                         } catch (InterruptedException e) {  
  40.                             e.printStackTrace();  
  41.                         }  
  42.                         System.out.println(Thread.currentThread().getName()+":::code卖出第"+(tick--)+"张票!");  
  43.                     }  
  44.                 }  
  45.             }  
  46.         }  
  47.         else  
  48.         {  
  49.             while(true)  
  50.                 show();  
  51.         }  
  52.     }  
  53. }  
  54. class ThisThreadDemo  
  55. {  
  56.     public static void main(String[] args)  
  57.     {  
  58.         TicketCenter tc = new TicketCenter();  
  59.           
  60.         //创建线程,将Runnable接口的子类对象作为参数传递给线程的构造方法,同时可以设置线程名称;  
  61.         Thread t1 = new Thread(tc,"窗口一");  
  62.         Thread t2 = new Thread(tc,"窗口二");  
  63.         //启动各个线程  
  64.         t1.start();//窗口一线程执行同步代码块;  
  65.           
  66.         //避免后面的flag赋值语句先于线程启动,无法让两个线程分别运行不同部分;  
  67.         //确保t1线程启动运行;  
  68.         try {  
  69.             Thread.sleep(10);  
  70.         } catch (InterruptedException e) {  
  71.             e.printStackTrace();  
  72.         }  
  73.         //切换标记  
  74.         tc.flag = false;  
  75.         t2.start();//窗口二进程执行同步函数;  
  76.     }  
  77. }  


静态同步函数:静态方法加载进内存时,没有本类对象,但是有该类对应的字节码文件对象:类名.class;该对象的类型是Class,用这个字节码文件对象作为静态同步函数的锁;

(8).死锁问题

死锁的特征:线程间同步中嵌套同步,持有的是不一样的锁,分别请求持有对方的锁;

编写一个死锁程序:


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.zr.day11;  
  2. //分配锁  
  3. class MyLock  
  4. {  
  5.     public static Object lockA = new Object();  
  6.     public static Object lockB = new Object();  
  7. }  
  8.   
  9. class DeadThread implements Runnable  
  10. {  
  11.     boolean flag = true;  
  12.     DeadThread(boolean flag)  
  13.     {  
  14.         this.flag = flag;  
  15.     }  
  16.     public void run()  
  17.     {  
  18.         if(flag)  
  19.         {  
  20.             //持有A锁,请求持有B锁  
  21.             while(true)  
  22.             {  
  23.                 synchronized(MyLock.lockA)  
  24.                 {  
  25.                     System.out.println(Thread.currentThread().getName()+"if -----  lockA");  
  26.                     synchronized(MyLock.lockB)  
  27.                     {  
  28.                         System.out.println(Thread.currentThread().getName()+"if -----  lockB");  
  29.                     }  
  30.                 }  
  31.             }  
  32.         }  
  33.         else  
  34.             //持有B锁,请求持有A锁  
  35.             while(true)  
  36.             {  
  37.                 synchronized(MyLock.lockB)  
  38.                 {  
  39.                     System.out.println(Thread.currentThread().getName()+"else -----  lockB");  
  40.                     synchronized(MyLock.lockA)  
  41.                     {  
  42.                         System.out.println(Thread.currentThread().getName()+"else -----  lockA");  
  43.                     }  
  44.                 }  
  45.             }  
  46.                   
  47.     }  
  48. }  
  49.   
  50. class DeadLockDemo   
  51. {  
  52.     public static void main(String[] args)  
  53.     {  
  54.         new Thread(new DeadThread(true)).start();  
  55.         new Thread(new DeadThread(false)).start();  
  56.     }  
  57. }  


5.线程间的通信

多个线程操作同一资源,但是操作的动作不同;
(1).等待唤醒机制
wait():释放资源,释放锁;
sleep():释放资源,不释放锁;
notify():唤醒某个等待的进程;
notifyAll():唤醒所有等待的进程;
wait,notify,notifyAll都是用在同步中:因为要对持有监视器(锁)的线程进行操作,只有同步才具有锁;
这些操作线程的方法都定义在Object类中:因为这些方法在操作同步中的线程时,必须标识它们所操作的线程所持有的锁,否则会出现非法的监视器状态异常;锁即对象,所以定义在Object类最合适;
同一个锁上被wait的线程可以被同一锁的notify唤醒;
等待和唤醒必须是同一个锁;
用静态修饰的变量也是共享数据,但是不建议声明静态,生存周期太长;

(2).生产者和消费者问题

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 实现对资源的生产和消费; 
  3.  */  
  4. package com.zr.day12;  
  5.   
  6. class Resource  
  7. {  
  8.     //定义一些属性;  
  9.     private String name;      
  10.     private String id;  
  11.     private int num;        //用来计数;  
  12.     private boolean flag;   //标记;  
  13.       
  14.     //主要由生产者线程调用执行;  
  15.     public synchronized void set()  
  16.     {  
  17.         //判断标记;相当于判断条件,当条件不满足时,就要等待;  
  18.         while(flag)  
  19.             try {  
  20.                 //wait(),notify(),notifyAll()这些方法在同步中使用时,必须标识它们所操作线程所属的锁对象;  
  21.                 //这里可以省略this;  
  22.                 this.wait();  
  23.             } catch (InterruptedException e) {  
  24.                 // TODO Auto-generated catch block  
  25.                 e.printStackTrace();  
  26.             }  
  27.         this.id = "NO"+num;  
  28.         this.name = "长江"+(num++)+"号";  
  29.         System.out.println(Thread.currentThread().getName()+"生产了"+this.name);  
  30.           
  31.         //切换标记;改变条件;  
  32.         flag = true;  
  33.           
  34.         //wait(),notify(),notifyAll()这些方法在同步中使用时,必须标识它们所操作线程所属的锁对象;  
  35.         //用来唤醒所有等待的线程;  
  36.         this.notifyAll();  
  37.     }  
  38.       
  39.     //主要由消费者线程调用执行  
  40.     public synchronized void get()  
  41.     {  
  42.         //判断标记,判断条件,当条件不满足时,就要等待;  
  43.         while(!flag)  
  44.             try {  
  45.                 wait();  
  46.             } catch (InterruptedException e) {  
  47.                 // TODO Auto-generated catch block  
  48.                 e.printStackTrace();  
  49.             }  
  50.         System.out.println(Thread.currentThread().getName()+"消费了"+this.name+"-------"+this.id);  
  51.         flag = false;  
  52.         notifyAll();  
  53.     }  
  54.       
  55. }  
  56.   
  57. //生产者线程执行的代码块;  
  58. class Producer implements Runnable  
  59. {  
  60.     Resource r;  
  61.     Producer(Resource r)  
  62.     {  
  63.         this.r = r;  
  64.     }  
  65.     public void run()  
  66.     {  
  67.         while(true)  
  68.         {  
  69.             r.set();  
  70.         }  
  71.     }  
  72. }  
  73.   
  74. //消费者线程执行的代码;  
  75. class Customer implements Runnable  
  76. {  
  77.     Resource r;  
  78.     Customer(Resource r)  
  79.     {  
  80.         this.r = r;  
  81.     }  
  82.     public void run()  
  83.     {  
  84.         while(true)  
  85.         {  
  86.             r.get();  
  87.         }  
  88.     }  
  89. }  
  90.   
  91. class ProducerAndCustomer  
  92. {  
  93.     public static void main(String[] args)  
  94.     {  
  95.         Resource r = new Resource();  
  96.         Producer pro = new Producer(r);  
  97.         Customer cus = new Customer(r);  
  98.         //分别创建两个消费者连个生产者线程并启动;  
  99.         new Thread(pro,"生产者A").start();  
  100.         new Thread(cus,"消费者A").start();  
  101.         new Thread(pro,"生产者B").start();  
  102.         new Thread(cus,"消费者B").start();  
  103.     }  
  104. }  

运行结果:


现在就生产者消费问题中的一些情况进行分析:

情况一:当分别只有一个生产者线程和一个消费者线程时,赋值动作和打印动作在同时操作共享数据,导致输出结果混乱;


解决办法:将操作共享数据的代码同步;


情况二:当分别只有一个生产者和消费者线程时,出现连续生产多次或者连续消费多次;


解决方法:添加标记,判断条件,同时引进等待唤醒机制;


情况三:当单个生产者和单个消费者之间操作资源成功后,考虑引进多个生产者和多个消费者;但是在多个线程的情况下,还是会出现和情况二一样的问题;


解决方法:循环判断标记;

定义while()循环判断标记:让被唤醒的线程再一次判断标记;


情况四:多个生产者和多个消费者,循环判断标记,会出现多个线程都死等的状态;


解决办法:notify唤醒只唤醒某一个线程,使用notifyAll唤醒所有线程;

定义notifyAll(),需要唤醒对方线程,使用notify容易出现只唤醒本方线程的情况,导致程序中所有线程都等待;


(3).JDK5.0出现的显式的锁机制

JavaAPI中包java.util.concurrent.locks为锁和等待条件提供一个框架的接口和类;

Java.util.concurrent.locks

|--interface:Condition;

将Object监视器方法(wait(),notify(),notifyAll())分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待;其中Lock替代了synchronized方法和语句的使用,Condition替代了Object监视器方法的使用;

await(),signal(),signalAll();

|--interface:Lock

|--已知实现类:ReentrantLock;

Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作;

void lock();--获取锁

Condition newCondition();--返回绑定到该锁实例的Condition实例

void unlock();--释放锁

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * 使用显式的锁机制实现生产者和消费者问题; 
  3.  */  
  4.   
  5. package com.zr.day12;   
  6. import java.util.concurrent.locks.*;  
  7. //创建资源对象  
  8. class Res  
  9. {  
  10.     //name和id属性  
  11.     private String name;  
  12.     private String id;  
  13.     //用于计数  
  14.     private int num;  
  15.     //标记位用来控制条件  
  16.     private boolean flag;  
  17.       
  18.     //显式的锁机制用来取代synchronized方法和语句;  
  19.     Lock lock = new ReentrantLock();  
  20.     //一个锁对象可以指定多个条件对象,用来取代wait(),notify(),notifyAll();  
  21.     Condition nofull = lock.newCondition();  
  22.     Condition noempty = lock.newCondition();  
  23.       
  24.     public void set() throws InterruptedException  
  25.     {  
  26.         //获取锁  
  27.         lock.lock();  
  28.         try  
  29.         {  
  30.             while(flag)  
  31.                 //本方线程(生产者)等待,释放锁,同时释放资源  
  32.                 nofull.await();  
  33.             this.id = "NO"+num;  
  34.             this.name = "UFO-"+(num++)+"号";  
  35.             System.out.println(Thread.currentThread().getName()+"---生产了---"+this.name);  
  36.             flag = true;  
  37.             //唤醒对方线程(消费者)  
  38.             noempty.signalAll();  
  39.         }  
  40.         //释放锁,一定会执行  
  41.         finally  
  42.         {  
  43.             lock.lock();  
  44.         }  
  45.     }  
  46.       
  47.     public void get() throws InterruptedException  
  48.     {  
  49.         //获取锁  
  50.         lock.lock();  
  51.         try  
  52.         {  
  53.             while(!flag)  
  54.                 本方线程(消费者)等待,释放锁,同时释放资源  
  55.                 noempty.await();  
  56.             System.out.println(Thread.currentThread().getName()+"--消费了--"+this.name+"----"+this.id);  
  57.             flag = false;  
  58.             //唤醒对方线程(生产者)  
  59.             nofull.signalAll();  
  60.         }  
  61.         //释放锁,一定会执行  
  62.         finally  
  63.         {  
  64.             lock.unlock();  
  65.         }  
  66.     }  
  67. }  
  68.   
  69. //定义生产者  
  70. class Pro implements Runnable  
  71. {  
  72.     Res r;  
  73.     Pro(Res r)  
  74.     {  
  75.         this.r = r;  
  76.     }  
  77.     public void run()  
  78.     {  
  79.         while(true)  
  80.         {  
  81.             try {  
  82.                 Thread.sleep(10);  
  83.                 r.set();  
  84.             } catch (InterruptedException e) {  
  85.                 e.printStackTrace();  
  86.             }  
  87.         }  
  88.     }  
  89. }  
  90.   
  91. //定义消费者  
  92. class Cus implements Runnable  
  93. {  
  94.     Res r;  
  95.     Cus(Res r)  
  96.     {  
  97.         this.r = r;  
  98.     }  
  99.     public void run()  
  100.     {  
  101.         while(true)  
  102.         {  
  103.             try {  
  104.                 Thread.sleep(10);  
  105.                 r.get();  
  106.             } catch (InterruptedException e) {  
  107.                 e.printStackTrace();  
  108.             }  
  109.         }  
  110.     }  
  111. }  
  112.   
  113. class LockAndCondition  
  114. {  
  115.     public static void main(String[] args)  
  116.     {  
  117.         Res r = new Res();  
  118.         Pro pro = new Pro(r);  
  119.         Cus cus = new Cus(r);  
  120.         new Thread(pro,"生产者A").start();  
  121.         new Thread(pro,"生产者B").start();  
  122.         new Thread(cus,"消费者A").start();  
  123.         new Thread(cus,"消费者B").start();  
  124.     }  
  125. }  


6.其他线程属性

(1).停止线程

早期停止线程的方法是调用stop方法,但是该方法已经过时;

现在停止线程只有一种方式,就是run方法执行完毕,同时由于开启多线程运行,运行代码通常是循环结构,因此只要控制住循环,就可以让run方法结束,同时线程也会停止;

(2).interrupt()方法:中断线程状态

当线程处于冻结状态时,可能读取不到标记,线程就不会停止;

当没有指定的方式让冻结的线程恢复到运行状态时,这是需要对冻结状态进行清除;强制让线程恢复到运行状态中来,这样就可以操作标记让线程停止;

Thread类中提供的这种清除线程冻结状态的方法是interrupt();

(3).join方法:

当A线程执行到了B线程的join()方法时,A线程会等待,直到B线程执行结束后(在此期间还可以和已经启动的其他线程并发执行,而并不是所有线程都等B线程执行结束),A才会重新参与运行;

某个线程等待另一个线程去死;

(4).守护线程:

public final void setDaemon(boolean on)

必须在启动线程前调用;

守护线程可以理解为后台线程,当程序中所有线程都是守护线程时,程序结束;

当所有的前台线程结束,后台线程自动结束;

(5).yield方法:暂停当前正在执行的线程对象,并执行其他线程;static void yield();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值