码迷,mamicode.com
首页 > 编程语言 > 详细

黑马程序员——总结——多线程——改12

时间:2014-12-29 22:43:19      阅读:300      评论:0      收藏:0      [点我收藏+]

标签:

  多线程

 

一、线程的概念

  线程时程序执行的控制单元,一个进程必须有一个以上的线程;多线程并发执行可以提高程序的效率,起到同时执行的效果!

  比如:电脑同时执行qq,迅雷;迅雷启动多个线程下载多个文件;窗口卖票

 

二、开启线程的方法

两种创建线程的方法:Extends Thread和implements Runnable

 

(1)、继承方法

    a,定义类继承Thread。

        b,复写Thread中的run方法。

             目的:将自定义代码存储在run方法中,让线程运行。

        c,创建定义类的实例对象。相当于创建一个线程。

        d,用该对象调用线程的start方法。该方法的作用是:启动线程,调用run方法。

 1 public class Demo2 {
 2 
 3     public static void main(String[] args) {
 4         MyThread mt = new MyThread();          //4,创建自定义类的对象
 5         mt.start();                            //5,开启线程
 6 
 7         for(int i = 0; i < 3000; i++) {
 8             System.out.println(Thread.currentThread().getName()+"bb");
 9         }//这个打印线程名字是main
10     }
11 }
12 class MyThread extends Thread {              //1,定义类继承Thread
13     public void run() {                      //2,重写run方法
14         for(int i = 0; i < 3000; i++) {      //3,将要执行的代码,写在run方法中
15             System.out.println(Thread.currentThread().getName()+"aaaaaaaaaaaaaaa");
16         }
17     }
18 }

 

(2)、实现方法

    a,定义类实现Runnable的接口。

        b,覆盖Runnable接口中的run方法。目的也是为了将线程要运行的代码存放在该run方法中。

        c,通过Thread类创建线程对象。

        d,将Runnable接口的子类对象作为实参传递给Thread类的构造方法

 1     public static void main(String[] args) {
 2         MyThread mt = new MyThread();          //4,创建自定义类的对象
 3         Thread th=new Thread(mt);
 4         th.start();                            //5,开启线程
 5 
 6         for(int i = 0; i < 3000; i++) {
 7             System.out.println(Thread.currentThread().getName()+"bb");
 8         }//这个打印线程名字是main
 9     }
10 }
11 class MyThread implements Runnable {              //1,定义类继承Thread
12     public void run() {                      //2,重写run方法
13         for(int i = 0; i < 3000; i++) {      //3,将要执行的代码,写在run方法中
14             System.out.println(Thread.currentThread().getName()+"aaaaaaaaaaaaaaa");
15         }
16     }
17 }

 

三、两种方法的区别

1、使用方法的区别:  

继承法:继承Thread,由于子类重写了Thread()类中run(),所以直接调用start()即可;

实现法:实现Runnable接口,构造函数中传入Runnable的引用,start()调用run ()方法是,判断成员变量Runnable的引用是否为空,

不为空编译时看的是Runnable的run(),运行时执行的是子类的run();

一般使用实现法:虽然代码较复杂,但避免了单继承的弊端,可以多实现,

 

2、线程的几种状态 (毕姥爷这里貌似有误,看别的资料获得)

  在Java当中,线程通常都有五种状态,创建、就绪、运行、阻塞和死亡。 
  第一是创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。 
  第二是就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。 
  第三是运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。 
  第四是阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。 
  第五是死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪。 

 3、使用匿名内部类实现线程;

继承匿名内部类法:

1 new Thread() {                                                  //1,new 类(){}继承这个类
2     public void run() {                                         //2,重写run方法
3         for(int i = 0; i < 3000; i++) {                         //3,将要执行的代码,写在run方法中
4             System.out.println("aaaaaaaaaaaaaaaaaaaaaaaaaaaa");
5         }
6     }
7 }.start();

实现匿名内部类法:

1 new Thread(new Runnable(){                                      //1,new 接口(){}实现这个接口
2     public void run() {                                         //2,重写run方法
3         for(int i = 0; i < 3000; i++) {                         //3,将要执行的代码,写在run方法中
4             System.out.println("bb");
5         }
6     }
7 }).start(); 

 

四、同步

1、需要同步解决

  a、当多线程并发,有多段代码同时执行时,我们不希望在某一段代码执行时CPU执行权更换到其他线程

  b、如果两段代码是同步的,那么同一时间只能执行一段,完全执行完才能执行另一个线程

方法:

2、同步代码块法;关键字:synchronized加上一个所对象来定义这一段代码

        多个同步代码块使用相同的锁为对象,那么这些线程就同步了

举个栗子:

卖票:当多线程操作共享数据时,可能发生安全问题,这时我们就要把操作数据的代码同步

 1 package TEST;
 2  2 /*编写三各类Ticket、SealWindow、TicketSealCenter分别代表票信息、售票窗口、售票中心。
 3  3  * 售票中心分配一定数量的票,由若干个售票窗口进行出售,利用你所学的线程知识来模拟此售票过程。
 4  4  */
 5  5 
 6  6 public class test_1Ticket {
 7  7 
 8  8     public static void main(String[] args) {
 9  9         
10 10         Ticket tk = new Ticket();//建立票信息对象
11 11         TicketSealCenter tsc = new TicketSealCenter(tk);// 创建售票中心。
12 12         tsc.set(200);//售票中心分配一定数量的票
13 13         
14 14         new Thread(new SealWindow(tk,"一号窗口")).start();// 创建、启动线程,开始卖票。
15 15         new Thread(new SealWindow(tk,"二号窗口")).start();
16 16         new Thread(new SealWindow(tk,"三号窗口")).start();
17 17         new Thread(new SealWindow(tk,"四号窗口")).start();
18 18                                 
19 19     }
20 20 }
21 21 class Ticket{
22 22     private static int ticket;
23 23     public static int getTicket() {
24 24         return ticket;
25 25     }
26 26     public static void setTicket(int ticket) {
27 27         Ticket.ticket = ticket;
28 28     }    
29 29 }
30 30 
31 31 
32 32 class TicketSealCenter{
33 33     Ticket tk=null;
34 34     TicketSealCenter(Ticket tk){
35 35         this.tk=tk;
36 36     }
37 37     
38 38     public void set(int t){//它可以设置票数
39 39         Ticket.setTicket(t);
40 40     }
41 41 }
42 42 
43 43 class SealWindow implements Runnable{
44 44     private String name=null;
45 45     private Ticket ticket;
46 46     SealWindow(Ticket ticket,String name){
47 47         this.name=name;
48 48         this.ticket=ticket;
49 49     }
50 50     public void run(){
51 51         while(true){
52 52             synchronized (ticket) {
53 53                 int t=ticket.getTicket();
54 54                 if(t>0){
55 55                     System.out.println(name+": 第"+(tsex="+sex);
29         
30         flag=false;
31         notify();
32     }    
33 }
34 //存线程
35 class Input implements Runnable{
36     private Resource r;
37     Input(Resource r){
38         this.r=r;
39     }
40     @Override
41     public void run() {
42         int x=0;
43         if(x==0){
44             r.setInput("张三","waman");
48         }
49         x=(x+1)%2;//if和else交叉打印
50     }    
51 }
52 //取线程
53 class Output implements Runnable{
54     private Resource r;
55     Output(Resource r){
56         this.r=r;
57     }
58     public void run(){
59         while(true){
60             r.getOutput();
61         }
62     }
63 }
64 
65 public class SourceDemo {
66     public static void main(String[] args){
67         Resource r=new Resource();//操作同一个资源
68         new Thread(new Input(r)).start();//存
69         new Thread(new Output(r)).start();//取
70     }
71     
72 }

1)wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?

                a,这些方法存在与同步中。

                b,使用这些方法时必须要标识所属的同步的锁。同一个锁上wait的线程,只可以被同一个锁上的notify唤醒。

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

        2)wait(),sleep()有什么区别?

              wait():释放cpu执行权,释放锁。

              sleep():释放cpu执行权,不释放锁。

        3)为甚么要定义notifyAll?

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

三.线程之间的通信

  • 1.什么时候需要通信
    • 多个线程并发执行时, 在默认情况下CPU是随机切换线程的
    • 如果我们希望他们有规律的执行, 就可以使用通信, 例如每个线程执行一次打印
  • 2.怎么通信
    • 调用wait(),使线程等待让出CPU
    • 调用notify(),唤醒等待的线程
    • 这两个方法必须在同步代码中执行, 并且使用同步锁对象来调用
  • 3.多个线程通信的问题
    • notify()方法是随机唤醒一个线程
    • notifyAll()方法是唤醒所有线程
    • JDK5之前无法唤醒指定的一个线程
    • 如果多个线程之间通信, 需要使用notifyAll()通知所有线程, 用while来反复判断条件

四.JDK5之后的线程控制

  • 1.同步
    • 使用ReentrantLock类的lock()和unlock()方法进行同步
  • 2.通信
    • 使用ReentrantLock类的newCondition()方法可以获取Condition对象
    • 需要等待的时候使用Condition的await()方法, 唤醒的时候用signal()方法
    • 不同的线程使用不同的Condition, 这样就能区分唤醒的时候找哪个线程了

五.同步与非同步类的

  同步安全效率低下        不同步不安全效率高

  StringBuffer         StringBuilder

  Vector             ArrayList

  Hashtable          HashMap

黑马程序员——总结——多线程——改12

标签:

原文地址:http://www.cnblogs.com/shuiyinmeizi/p/4192443.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!