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

一、多线程概述

        要理解多线程,就必须理解线程。而要理解线程,就必须知道进程。

1、 进程

        是一个正在执行的程序。例如,qq等

        每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。//例如登录qq,你需要输入账号、密码、点击登录等

2、线程

         就是进程中的一个独立的控制单元。线程在控制着进程的执行。只要进程中有一个线程在执行,进程就不会结束。//登录qq时,会验证密码账号等线程

        一个进程中至少有一个线程。即  进程:线程=1:n(n为大于等于1的整数)

3、多线程

        在java虚拟机启动的时候会有一个java.exe的执行程序,也就是一个进程。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。JVM启动除了执行一个主线程,还有负责垃圾回收机制的线程。像这种在一个进程中有多个线程执行的方式,就叫做多线程。

4、多线程存在的意义

        多线程的出现能让程序产生同时运行效果。可以提高程序执行效率。

         例如:在java.exe进程执行主线程时,如果程序代码特别多,在堆内存中产生了很多对象,而同时对象调用完后,就成了垃圾。如果垃圾过多就有可能是堆内存出现内存不足的现象,只是如果只有一个线程工作的话,程序的执行将会很低效。而如果有另一个线程帮助处理的话,如垃圾回收机制线程来帮助回收垃圾的话,程序的运行将变得更有效率。现实生活中,你可以一边烧水,一边切菜,为了做汤。

5、计算机CPU的运行原理

         我们电脑上有很多的程序在同时进行,就好像cpu在同时处理这所以程序一样。但是,在一个时刻,单核的cpu只能运行一个程序。而我们看到的同时运行效果,只是cpu在多个进程间做着快速切换动作。就好像灯泡一样,它是在不停的闪灭,而我们眼睛接受不全,便以为是一直亮。

         而cpu执行哪个程序,是毫无规律性的。这也是多线程的一个特性:随机性。哪个线程被cpu执行,或者说抢到了cpu的执行权,哪个线程就执行。而cpu不会只执行一个,当执行一个一会后,又会去执行另一个,或者说另一个抢走了cpu的执行权。至于究竟是怎么样执行的,只能由cpu决定。就好像皇帝跟妃子的关系。

 

二、创建线程的方式

        创建线程共有两种方式:继承方式和实现方式(简单的说)。

1、 继承方式

        通过查找java的帮助文档API,我们发现java中已经提供了对线程这类事物的描述的类——Thread类。这第一种方式就是通过继承Thread类,然后复写其run方法的方式来创建线程。

创建步骤:

        a,定义类继承Thread。

        b,复写Thread中的run方法。

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

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

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

注:如果对象直接调用run方法,等同于只有一个线程在执行,自定义的线程并没有启动。

覆盖run方法的原因:

        Thread类用于描述线程。该类就定义了一个功能,用于存储线程要执行的代码。该存储功能就run方法。也就是说,Thread类中的run方法,用于存储线程要运行的代码。

程序示例:

 1 /*
 2 
 3 创建两个线程 贡献一个 资源,
 4 加入主线程  便有三个线程
 5 */
 6 class Demo extends Thread//继承Thread类
 7 {
 8      public int x=60;//定义计数器
 9     public Demo(String name)
10     {
11         super(name);
12     }
13     public void run()//复写run方法
14     {
15 
16         while(x>0)
17         {
18             System.out.println(Thread.currentThread().getName()+".....sale:"+"...."+x);//获取线程名称以及对应的次数
19                     x--;
20         }
21     }
22 }
23 class ThreadDemo
24 {
25     public static void main(String[] args)
26     {
27         Demo d=new Demo("--one--");//创建线程名称
28         Thread d1=new Thread(d);//共用一个资源的两个线程
29         Thread d2=new Thread(d);
30         
31         d1.start();//start()方法可以调用run()方法 实现多线程
32         d2.start();
33 
34         
35             for(int x=0;x<60;x++)//主线程  打印  main
36             {
37                 System.out.println("---main---"+x);
38             }
39         
40     }
41 }

程序运行结果每一次都可能出现不同种的情况,而下图出现的次序错乱问题,应该是计算机为多核,打印到显示台上才会如此。如图:

第二种方式:实现Runnable接口:

 1 /*
 2 需求:简单的卖票程序。
 3 多个窗口同时买票。
 4 
 5 
 6 创建线程的第二种方式:实现Runable接口
 7 
 8 步骤:
 9 1,定义类实现Runnable接口
10 2,覆盖Runnable接口中的run方法。
11     将线程要运行的代码存放在该run方法中。
12 
13 3,通过Thread类建立线程对象。
14 4,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
15     为什么要将Runnable接口的子类对象传递给Thread的构造函数。
16     因为,自定义的run方法所属的对象是Runnable接口的子类对象。
17     所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
18 
19 
20 5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
21 
22 
23 
24 实现方式和继承方式有什么区别呢?
25 
26 实现方式好处:避免了单继承的局限性。
27 在定义线程时,建立使用实现方式。
28 
29 两种方式区别:
30 继承Thread:线程代码存放Thread子类run方法中。
31 实现Runnable,线程代码存在接口的子类的run方法。
32 
33 
34 
35 
36 */
37 
38 class Ticket implements Runnable//extends Thread
39 {
40     private  int tick = 100;//有一百张票
41     public void run()
42     {
43         while(true)
44         {
45             if(tick>0)
46             {
47                 System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);//打印线程名称跟票数
48             }
49             else
50                 break;
51         }
52     }
53 }
54 
55 
56 class  TicketDemo
57 {
58     public static void main(String[] args) 
59     {
60 
61         Ticket t = new Ticket();
62 
63         Thread t1 = new Thread(t);//创建了一个线程;
64         Thread t2 = new Thread(t);//创建了一个线程;
65         Thread t3 = new Thread(t);//创建了一个线程;
66         Thread t4 = new Thread(t);//创建了一个线程;
67         t1.start();//运行该线程,调用run()方法
68         t2.start();
69         t3.start();
70         t4.start();
71 
72 
73         /*
74         Ticket t1 = new Ticket();
75         //Ticket t2 = new Ticket();
76         //Ticket t3 = new Ticket();
77         //Ticket t4 = new Ticket();
78 
79         t1.start();
80         t1.start();
81         t1.start();
82         t1.start();
83         */
84 
85     }
86 }

三、两种方式的区别和线程的几种状态

1、两种创建方式的区别

        继承Thread:线程代码存放在Thread子类run方法中。

        实现Runnable:线程代码存放在接口子类run方法中。      

2、几种状态

        被创建:等待启动,调用start启动。

         运行状态:具有执行资格和执行权。

         临时状态(阻塞):有执行资格,但是没有执行权。

         冻结状态:遇到sleep(time)方法和wait()方法时,失去执行资格和执行权,sleep方法时间到或者调用notify()方法时,获得执行资格,变为临时状态。

         消忙状态:stop()方法,或者run方法结束。

注:当已经从创建状态到了运行状态,再次调用start()方法时,就失去意义了,java运行时会提示线程状态异常。

四、线程安全问题

 1 /*
 2 通过分析,发现,打印出0,-1,-2等错票
 3 
 4 多线程的运行出现了问题
 5 问题的原因:
 6         当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,
 7         另一个线程参与进来执行。导致共享数据的错误
 8 
 9 解决办法:
10         对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
11 
12 
13 java对于多线程的安全问题提供了专业的解决方式。
14 就是同步代码块
15 synchronized(对象)
16 {
17     需要被同步的代码
18 }
19 对象如同锁。持有锁的线程可以在同步中执行。
20 没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有 获取锁
21 
22 火车上的卫生间----经典
23 
24 同步的前提:
25 1    必须要有两个或者两个以上的线程
26 2    必须是多个线程使用同一个锁
27 
28 
29 必须保证同步中只能有一个线程在运行
30 
31 好处:    解决了多线程的安全问题
32 
33 弊端:    多个线程需要判断锁,较为消耗资源 
34 
35 
36 */
37 
38 class Ticket implements Runnable
39 {
40     private int tick=100;
41     Object obj =new Object();
42     public void run()
43     {
44         while(true)
45         {
46             synchronized(obj)//加锁,锁可以是任意存在的对象
47             {
48                 if(tick>0)
49                 {
50                     //try{Thread.sleep(10);}catch(Exception e){}//线程暂时休眠
51                     System.out.println(Thread.currentThread().getName()+"....sale:"+tick--);//打印线程名称和票编号
52                 }
53                 else 
54                     break;
55             }
56         }
57     }
58 }
59 class TicketDemo2
60 {
61     public static void main(String[] args)
62     {
63         
64         Ticket t = new Ticket();
65 
66         Thread t1 = new Thread(t);
67         Thread t2 = new Thread(t);
68         Thread t3 = new Thread(t);
69         Thread t4 = new Thread(t);
70         t1.start();
71         t2.start();
72         t3.start();
73         t4.start();
74     }
75 }

五、静态函数的同步方式

如果同步函数被静态修饰后,使用的锁是什么呢?

        通过验证,发现不在是this。因为静态方法中也不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。如:

        类名.class 该对象的类型是Class

这就是静态函数所使用的锁。而静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class

经典示例:

 1 /*
 2 单例设计模式。
 3 
 4 
 5 */
 6 //饿汉式
 7 class Single
 8 {
 9     private static final Single s=new Single();//创建Single类并分配资源
10     private  Single(){}
11     public static Single getInstance()
12     {
13         return s;
14     }
15 }
16 
17 
18 //懒汉式
19 class Single
20 {
21     private static Single s=null;//创建Single类,使其为空
22     private Single(){}
23     public static Single getInstance()
24     {
25         if (s==null)
26         {
27             synchronized(Single.class)//Single.class文件在类创建时,便存在
28             {
29                 if(s==null)
30                     s=new Single();//分配资源
31             }
32         }
33         return s;
34     }
35 }

六、死锁

        当同步中嵌套同步时,就有可能出现死锁现象。

 1 /*
 2 死锁
 3 同步中嵌套同步。A锁套B锁,B锁套A锁。
 4 */
 5 class Test implements Runnable
 6 {
 7     private boolean flag;
 8     Test(boolean flag)
 9     {
10         this.flag = flag;
11     }
12 
13     public void run()
14     {
15         if(flag)
16         {
17             while(true)
18             {
19                 synchronized(MyLock.locka)//A锁
20                 {
21                     System.out.println(Thread.currentThread().getName()+"...if locka ");
22                     synchronized(MyLock.lockb)//B锁
23                     {
24                         System.out.println(Thread.currentThread().getName()+"..if lockb");                    
25                     }
26                 }
27             }
28         }
29         else
30         {
31             while(true)
32             {
33                 synchronized(MyLock.lockb)//B锁
34                 {
35                     System.out.println(Thread.currentThread().getName()+"..else lockb");
36                     synchronized(MyLock.locka)//A锁
37                     {
38                         System.out.println(Thread.currentThread().getName()+".....else locka");
39                     }
40                 }
41             }
42         }
43     }
44 }
45 
46 
47 class MyLock//创建两个锁
48 {
49     static Object locka = new Object();
50     static Object lockb = new Object();
51 }
52 
53 class  DeadLockTest
54 {
55     public static void main(String[] args) 
56     {
57         Thread t1 = new Thread(new Test(true));//创建两个线程
58         Thread t2 = new Thread(new Test(false));
59         t1.start();//运行线程,调用run()方法
60         t2.start();
61     }
62 }

死锁运行程序 结果如下图:

七、线程间通信

        其实就是多个线程在操作同一个资源,但是操作的动作不同。如一个 存入,一个输出。

1、使用同步操作同一资源的示例:

  1 /*
  2 线程间通讯:
  3 其实就是多个线程在操作同一个资源,
  4 但是操作的动作不同。
  5 
  6 */
  7 class Res//定义资源 包括姓名 性别  以及否的初设定
  8 {
  9     String name;
 10     String sex;
 11     boolean flag = false;
 12 }
 13 
 14 class Input implements Runnable//存入  继承Runnable
 15 {
 16     private Res r ;
 17     Input(Res r)
 18     {
 19         this.r = r;
 20     }
 21     public void run()//复写run方法
 22     {
 23         int x = 0;//定义变量,改变存入资源的种类
 24         while(true)
 25         {
 26             synchronized(r)//
 27             {
 28 
 29                 if(r.flag)//当有资源。flag为真时,先等待,避免多存
 30                     try{r.wait();}catch(Exception e){}
 31                 if(x==0)
 32                 {
 33                     r.name="mike";
 34                     r.sex="man";
 35                 }
 36                 else
 37                 {
 38                     r.name="丽丽";
 39                     r.sex = "女女女女女";
 40                 }
 41                 x = (x+1)%2;//改变x的奇偶
 42                 r.flag = true;//改变flag的值,说明已存
 43                 r.notify();//唤醒其他线程
 44             }
 45         }
 46     }
 47 }
 48 
 49 class Output implements Runnable//输出    继承Runnable
 50 {
 51     private Res r ;
 52     
 53     Output(Res r)
 54     {
 55         this.r = r;
 56     }
 57     public void run()//复写run方法
 58     {
 59         while(true)
 60         {
 61             synchronized(r)//
 62             {
 63                 if(!r.flag)//当flag为假时,即没有资源时,先等待
 64                     try{r.wait();}catch(Exception e){}
 65                 System.out.println(r.name+"...."+r.sex);//打印资源
 66                 r.flag = false;//改变flag的值,说明已取出
 67                 r.notify();//唤醒其他线程
 68             }
 69         }
 70     }
 71 }
 72 
 73 
 74 class  InputOutputDemo
 75 {
 76     public static void main(String[] args) 
 77     {
 78         Res r = new Res();
 79 
 80         Input in = new Input(r);
 81         Output out = new Output(r);
 82 
 83         Thread t1 = new Thread(in);
 84         Thread t2 = new Thread(out);
 85 
 86         t1.start();
 87         t2.start();
 88     }
 89 }
 90 
 91 
 92 //notifyAll();
 93 
 94 /*
 95 wait:
 96 notify();
 97 notifyAll();
 98 
 99 都使用在同步中,因为要对持有监视器(锁)的线程操作。
100 所以要使用在同步中,因为只有同步才具有锁。
101 
102 为什么这些操作线程的方法要定义Object类中呢?
103 因为这些方法在操作同步中线程时,都必须要标识它们所操作线程只有的锁,
104 只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。
105 不可以对不同锁中的线程进行唤醒。
106 
107 也就是说,等待和唤醒必须是同一个锁。
108 
109 而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。
110 
111 
112 */

程序运行部分截图如下:

 几个小问题:

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

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

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

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

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

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

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

        3)为甚么要定义notifyAll?

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

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

        将同步synchronized替换成显示的Lock操作。将Object中wait,notify,notifyAll,替换成了Condition对象。该Condition对象可以通过Lock锁进行获取,并支持多个相关的Condition对象。

升级解决方案的示例:

  1 import java.util.concurrent.locks.*;
  2 
  3 class ProducerConsumerDemo2 
  4 {
  5     public static void main(String[] args) 
  6     {
  7         Resource r = new Resource();//建立资源
  8 
  9         Producer pro = new Producer(r);//创建生产者与消费者,操作的是同一个资源
 10         Consumer con = new Consumer(r);
 11 
 12         Thread t1 = new Thread(pro);//生产者两个线程
 13         Thread t2 = new Thread(pro);
 14         Thread t3 = new Thread(con);//消费者两个线程
 15         Thread t4 = new Thread(con);
 16 
 17         t1.start();//运行线程,调用run()方法
 18         t2.start();
 19         t3.start();
 20         t4.start();
 21 
 22     }
 23 }
 24 
 25 /*
 26 JDK1.5 中提供了多线程升级解决方案。
 27 将同步Synchronized替换成现实Lock操作。
 28 将Object中的wait,notify notifyAll,替换了Condition对象。
 29 该对象可以Lock锁 进行获取。
 30 该示例中,实现了本方只唤醒对方操作。
 31 Lock:替代了Synchronized
 32     lock 
 33     unlock
 34     newCondition()
 35 
 36 Condition:替代了Object wait notify notifyAll
 37     await();
 38     signal();
 39     signalAll();
 40 */
 41 class Resource
 42 {
 43     private String name;//创建资源固有属性
 44     private int count = 1;
 45     private boolean flag = false;
 46             //  t1    t2
 47     private Lock lock = new ReentrantLock();//创建锁
 48 
 49     private Condition condition_pro = lock.newCondition();//将锁的情况 分为两份,线程使用的是自己派别的锁
 50     private Condition condition_con = lock.newCondition();
 51 
 52 
 53 
 54     public  void set(String name)throws InterruptedException
 55     {
 56         lock.lock();
 57         try
 58         {
 59             while(flag)//flag  为真时,代表有资源
 60                 condition_pro.await();//t1,t2  生产者等待
 61             this.name = name+"--"+count++;
 62 
 63             System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name);//打印线程信息到控制台上
 64             flag = true;
 65             condition_con.signal();//唤醒消费者
 66         }
 67         finally
 68         {
 69             lock.unlock();//释放锁的动作一定要执行。
 70         }
 71     }
 72 
 73 
 74     //  t3   t4  
 75     public  void out()throws InterruptedException
 76     {
 77         lock.lock();
 78         try
 79         {
 80             while(!flag)//flag 为假时, 代表没有资源
 81                 condition_con.await();//消费者等待
 82             System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name);//打印线程信息到控制台上
 83             flag = false;
 84             condition_pro.signal();//唤醒生产者
 85         }
 86         finally
 87         {
 88             lock.unlock();//释放锁
 89         }
 90         
 91     }
 92 }
 93 
 94 class Producer implements Runnable//生产者的类,继承Runnable
 95 {
 96     private Resource res;
 97 
 98     Producer(Resource res)
 99     {
100         this.res = res;
101     }
102     public void run()  //复写run()方法
103     {
104         while(true)
105         {
106             try
107             {
108                 res.set("+商品+");  //在资源中增加商品
109             }
110             catch (InterruptedException e)
111             {
112             }
113             
114         }
115     }
116 }
117 
118 class Consumer implements Runnable//消费者的类,继承Runnable
119 {
120     private Resource res;
121 
122     Consumer(Resource res)
123     {
124         this.res = res;
125     }
126     public void run()//复写 run()方法
127     {
128         while(true)
129         {
130             try
131             {
132                 res.out();//移除资源
133             }
134             catch (InterruptedException e)
135             {
136             }
137         }
138     }
139 }

改程序运行结果部分截图如下:

八、停止线程

 1 /*
 2 stop方法已经过时
 3 
 4 如何停止线程?
 5 只有一种,run方法结束
 6 开启多线程运行,运行代码通常是循环结构
 7 
 8 只要控制住循环,就可以让run方法结束,也就是线程结束
 9 就是将while等判断语句中加入限制条件让其运行一段时间后停止运行
10 
11 特殊情况:
12 当线程处于冻结状态就不会读取到标记。那么线程就不会结束
13 例如线程就如等待状态,没有其他唤醒线程,将会导致线程运行不到后面的结束 语句
14 
15 当没有指定的方式让冻结的线程恢复到运行状态时,就需要对冻结了的线程进行清除
16 强制让线程恢复到运行状态中来,这样就可以操作标记让线程结束。
17 
18 强制的方法为中断  interrupt();   
19 例如
20 当主函数运行将要结束时,使进入冻结状态的t1进程,强制将其终止
21 t1.interrupt();
22 
23 
24 */
25 
26 
27 /*
28 该函数有三个线程,主线程和t1和t2两个线程。  两个线程中while可能会进入死循环,需要强制结束,
29 第一种方法:    通过调用,是while中的条件不满足,这时程序会结束
30 第二种方法:    使用中断函数interrupt();
31 */
32 class StopThread implements Runnable
33 {
34     private boolean flag =true;
35     public  void run()
36     {
37         while(flag)
38         {
39             
40             System.out.println(Thread.currentThread().getName()+"....run");
41         }
42     }
43     public void changeFlag()
44     {
45         flag = false;
46     }
47 }
48 
49 
50 
51 
52 class  StopThreadDemo
53 {
54     public static void main(String[] args) 
55     {
56         StopThread st = new StopThread();//新建停止线程
57         
58         Thread t1 = new Thread(st);
59         Thread t2 = new Thread(st);
60 
61 
62         //t1.setDaemon(true);//设置为守护线程,
63         //t2.setDaemon(true);
64         t1.start();
65         t2.start();
66 
67         int num = 0;
68 
69         while(true)
70         {
71             if(num++ == 60)
72             {
73                 st.changeFlag();//当满足条件时改变Flag状态,使线程结束
74                 //t1.interrupt();////清除冻结状态 ,唤醒线程
75                 //t2.interrupt();
76                 break;
77             }
78             System.out.println(Thread.currentThread().getName()+"......."+num);
79         }
80         System.out.println("over");
81     }
82 }

程序运行结果部分截图如下:

扩展小知识:

 1 /*
 2 目的:  学习join的运用
 3 join:
 4     当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行
 5     join可以 用来临时加入线程执行。
 6 
 7 即在主函数中,出现t1.join()时,只能等t1线程执行完成后,才能执行主函数中的线程
 8 如果在t1线程中加入了  t2.join()时,只能等t2线程执行完成后,才能执行主函数中的线程
 9 
10 注意:   t1.join()如果在主函数中出现,只与t1和主线程有关
11 
12 当t1有可能进入冻结状态时,可以使其中断,之后便可以执行主函数。
13     假使又使t1唤醒进入执行状态,将出现  异常
14 
15 
16 
17 
18 同步的线程中   cpu决定哪个线程将会执行,然而执行将会有一个频率,就是线程具有优先级
19 
20 从1到10;默认线程的优先级为5;10为最高,1为最低;
21 因为是常量  为了阅读性 MAX_PRIORITY     max_priority
22                         MIN_PRIORITY    min_priority
23                         MORM_PRIORITY    morm_priority
24         改变线程的优先级        t1.setPriority(Thread.MAX_PRIORITY);
25 
26 MAX_PRIORITY 
27 MIN_PRIORITY
28 MORM
29 
30 Thread.yield();   线程休息一下,将cpu的使用权,推给其他线程
31 Thread.yield();    Thread.yield  thread.yield    
32 */
33 class Demo implements Runnable
34 {
35     public void run()
36     {
37         for(int x=0;x<70;x++)
38         {
39             System.out.println(Thread.currentThread+"........"+x);\
40             Thread.yield();
41             
42         }
43     }
44 }
45 class JoinDemo
46 {
47     public static void main(String[]  args) throws Exception
48     {
49         Demo d=new Demo();
50         Thread t1=new Thread(d);
51         Thread t2=new Thread(d);
52         t1.start();
53         //t1.setPriority(Thread.MAX_PRIORITY);
54         t2.start();
55         //t1.join();
56         for(int x=0;x<80;x++)
57         {
58             //System.out.println("main...."+x);
59         }
60         System.out.println("over");
61     }
62 
63 }

自我总结:

     线程的由来是继承Thread或者实现Runnable,单线程可以认为是要做完某事才能做另外一件事,因此很负责很安全,同样的会导致效率比较慢,而多线程,可以看做同时做很多事,因此效率会比较高,但同时可能会带来安全隐患,因此需要用到锁这样的工具。

如果是很多人来做一件事情时,就需要有标记即flag,需要告诉别人,自己完成到哪儿了。

而有时某些人会陷入困境中,做很多无用功,这时需要别人来制止它的行为。即,强制结束。

而某些人会陷入迷茫,无事可做。要么唤醒它,要么结束它。

甚至会有那么一些人陷入内乱,导致无法工作,即死锁。这是我们要避免的情况。

而有些人是为了某些人的存在而存在的。即守护线程。如果某些人不存在了,那么有些人也就没有戏了。

而有些毒瘤的人加入某组织,毒瘤不死,组织无法运行。即  join()

posted on 2015-08-09 16:38  勿忘ing  阅读(212)  评论(0编辑  收藏  举报

导航