A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 臧盼 中级黑马   /  2012-12-21 21:02  /  1555 人查看  /  2 人回复  /   1 人收藏 转载请遵从CC协议 禁止商业使用本文

线程的概述
  1.什么是进程?
     正在运行的程序叫进程!

  2.OS为每个进程分配内存
     进程之间没有共享数据,都有自己独立的内存可以使用。

  3.进程的代码谁来执行
   进程中执行代码的是线程

  4.进程中同时执行的多个任务
     就是多线程。
     如果进程中只有一个任务需要执行,那么就是单线程了。

  5.线程的概念
        进程在执行过程中,可能需要多个任务同时执行,每个任务的执行者就是线程。线程     就是进程中的一个执行控制单元。

  6.多线程的用途
     同时执行多个任务,可以提高效率。
     可以完成多个任务并发的效果。

  7.JVM是一个进程,它也是多线程的。一个主线程需要去执行main,还要有一个线程去执行垃圾回收工作。

  8.线程是部分,进程是整体
     线程是部分,部分不能单独存在,它依赖与进程(整体)。
     线程不能单独启动,而进程可以。
     一个进程中的多个线程可以共享数据,而进程与进程之间不会共享数据。

创建多线程方式一:继承Thread类
  1.怎么写一个多线程程序
              1 给线程添加任务
           为?Thread类写一子类,并重写父类的run()方法。
        2 创建子类对象
        3 调用子类对象的start()方法。;

  2.多个进程同时运行是假的!多个线程同时运行也是假的!

  3.线程的随机性,以及时间分片
        CPU不是按顺序给线程插电,而是随机的。
        CPU会给一个线程插电之后,只给一小段时间,如果这个时间用完了,那么就拔电!不
      同的OS时间分的长短不一样。
      通常一个线程在没有用完时间片时就主动休眠了

  4.同一个多线程程序多次运行,结果可能不一样
      就是因为线程的随机性!就是CPU就是这么随机的,没有顺序的!

  5.主线程结束时,JVM是否结束
      当主线程结束时,其他还有线程还没结束,那么JVM是否结束。
   只要有线程,那么JVM就不会结束。

线程状态
  1.线程的生命周期包含状态
     新状态:
         Thread th = new Thread();
         th就是新状态。在th还没有被调用start()方法之前它是新状态!
     就绪状态(可运行状态)
         th.start()就绪状态。
         就绪状态包含了运行状态。你有可能是在运行,也有可能等待着CPU来插电!
     运行状态
         CPU给你插电了,你就运行了。
         运行的线程最好去主动进入阻塞状态。这样不用时间片用完了就可以给你断电了。
     阻塞状态
          休眠:Thread.sleep()方法时,那么这个线程就进入了休眠状态,它如果想回到就
       绪状态就必须睡足了指定的时间。
          等待:wait(),当一个线程执行了wait()方法后,那么它就进入了一个监视器对象
       的等待列表中,这时它再也醒不过来了,只能等待另一个线程去唤醒它。
         挂起: 当调用线程对象的suspend()方法后,这个线程就挂起了,要想回到就绪需要
       调用这个线程的resume()就回到了就绪状态。但suspend()和resum()都过时了。
          IO阻塞:当一个线程在进入IO操作时,那么就一定要等待到IO操作完毕才能回到就       绪状态。
           锁定:当一个线程想上厕所,但厕所有人,那么这个线程锁定了。这个线程需要等
       待到前一个线程释放对象锁,然后这个线程获取到了对象锁才能回到就绪状态。
    死亡状态
       正常死亡:当run()方法结束了,线程就死了。
       非正常死亡:因为没有抓捕到的异常导致线程突然死亡。只能是RuntimeExeption
       被杀了:有人调用了线程的stop()。这个方法已经被作废了,你不应该去使用它。

创建多线程方二:实现Runnable接口
      Runnable接口有一个run()方法,它是很简单的东西。实现它只需要重写run()方法就可以了。
      因为Runnable接口不是Thread类,所以这时想获取当前线程需要使Thread.currentThread()。

实例:
   售票
        实现多窗口同时售票!
        多个窗口共享同一票库,当票库无票时,程序结束。
public class Test {
        public static void main(String[] args) {
                PiaoKu pk = new PiaoKu(100);
                // 四个窗口共享一个票库
                ChuangKou ck1 = new ChuangKou(pk);
                ChuangKou ck2 = new ChuangKou(pk);
                ChuangKou ck3 = new ChuangKou(pk);
                ChuangKou ck4 = new ChuangKou(pk);
               
                Thread th1 = new Thread(ck1, "a");
                Thread th2 = new Thread(ck2, "b");
                Thread th3 = new Thread(ck3, "c");
                Thread th4 = new Thread(ck4, "d");
               
                th1.start();
                th2.start();
                th3.start();
                th4.start();
        }
}

// 售票窗口
class ChuangKou implements Runnable {
        private PiaoKu pk;//每个窗口都需要有一个票库
       
        // 创建窗口时给窗口传递一个票库对象
        public ChuangKou(PiaoKu pk) {
                this.pk = pk;
        }
       
        public void run() {
                String name = Thread.currentThread().getName();
                while(true) {
                        //当票库的票数小于等于0时,跳出循环。
                        if(pk.getCount() <= 0) {
                                break;
                        }
                        try {
                                Thread.sleep(20);
                        } catch (InterruptedException e) {
                        }
                        // 出票,并打印票码
                        System.out.println(name + ": " + pk.chuPiao());
                }
        }
}

// 票库
class PiaoKu {
        private int count;
       
        public PiaoKu(int count) {
                this.count = count;
        }
       
        // 获取当前票库的票数
        public int getCount() {
                return count;
        }
       
        // 出票,在出票同时还会返回当前出的是倒数第几张
        public int chuPiao() {
                int cnt = this.count--;
                return cnt;
        }
}

评分

参与人数 1技术分 +1 收起 理由
崔政 + 1

查看全部评分

2 个回复

倒序浏览
同步互斥
        同步的目的:用来处理多个线程共享同一数据时,所造成的错误。
        ?当多个线程之间存在共享数据的争用时,那么就需要使用同步互斥。
        当共享数据没有人使用时,那么第一个使用的线程就独占了它,这时第二个线程就不能在使用它了!只能等待前一个线程使用结束,共享数据空闲了,这时第二个线程才能使用。
        局部变量每个线程都有自己的一份。
        属性就不一对了。属性通常是共享的。
synchronized块的语法格式
        synchronized(监视器对象) {
            ……
        }
       
        监视器对象一般就是共享数据。
        多个同步块中使用相同的监视器对象,那就会出现同步互斥的效果!
       
监视器对象是属性,而不是局部变量。
这个属性还需要外界完成赋值。

public class A {
        Object monitor;//监视器对象
        public A(Object monitor m) {
          this.monitor = m;
        }
        public void xxx() {
          synchronized(monitor) {
          …
        }
        }
}

3 synchronized块的作用
        理解:
把监视器对象理解为导游;
把同步块中的代码理解为景区内容;
把线程理解为游客!
        当游客想进入景区游玩时,需要有导游陪同,如果a游客来到景区时,导游空闲,那么导游会陪同a游客进入景区,在a游客没有离开景区之前,b游客也要进入景区,那么这时导游不在,所以b游客需要等待导游空闲。当a游客离开了景区,那么导游就空闲了,这时b游客就可以由导游陪同进入景区了。
        a和b两个线程在同步块内容上同步互斥了,当a进入同步块后,b需要等待a退出后才能进入同步块。
synchronized方法

1 使用synchronized关键字来修饰方法
public synchronized void fun() {
}

        监视器对象:当前对象。
        景区:所有的同步方法内容都景区。
        同一个类的所有同步方法,很可能会用同一个当前对象,即同一个监视器对象。
        a线程调用了x对象的fun1()方法,那么a线程就占用了x对象。这时b线程要来调用x对象的fun2()方法,这时x对象已经被占用了,那么b线程就需要等待a线程释放x对象。
       
        一个对象的非同步方法不需要等待监视器对象!

2 注意,synchronized方法与synchronized块是相同的道理
        对同步块而言,监视器对象是圆包括中的对象。
        对同步方法而言,监视器对象是当前对象。
       
        多个同步块使用相同的监视器对象,那么这些执行同步块的线程就会同步互斥。
  调用同一个对象的同步方法(不一定同一个方法)时,那么多个调用方法的线程就同步互斥了。
静态同步方法

1 静态同步方法是可以存在的
        同一个类中的静态同步方法会有同步互斥效果!
        但不同类的中静态同步方法就不会有同步互斥效果了!

2 静态同步方法的监视器对象
        静态同步方法的监视器对象就是当前类的对象,即Class对象。
        class A {
           static synchronized fun1() {}
        static synchronized fun1() {}
        }
这两个方法的监视器对象都是A.class

3  单例模式中懒汉式的隐患

双重判断
If(instance == null) {
        synchronized(Single.class) {
        if(instance == null) {
        instance = new Single();
        }
        }
}
return instance;

死锁

1 什么叫死锁
        张三在A电话亭想去B电话亭
        李四在B电话亭想去A电话亭

2 理解死锁
  因为线程都很执着,如果有共享资源已被占用,那么线程会一直等待下去!如果两个线程,相互想使用对方的资源,在没有得到对方资源之前,还不会释放自己的资源,这时这两个线程就都不会再向下运行,这就死锁了!

2 通常死锁是怎么造成的
  同步嵌套调用造成的。

线程间的通讯

1 什么叫线程间的通讯
        多个线程之间需要协调工作。
        例如:
       
        1 女:我们结婚吧;
        2 男:我还没有钱;你等我,我去赚钱!
3 女:ok,那我wait了,你赚了钱回来,不要忘了notify一下我。

通讯原理

1 使用wait()、notify()、notifyAll()完成通讯
  我们称这三个方法为通讯方法,这三个方法是Object类的方法。

2 同步与线程间通讯方法
        所有的通讯方法必须在同步环境下使用。
        同步块内部可以调用这三个方法
        同步方法内部可以调用这三个方法。

3 监视器对象的监狱
        当调用a.wait()时,那么当前线程就进入了这个监视器对象的监狱!
        每个监视器对象都有自己的监狱。
        如果想把这个线程释放出来(唤醒),应该让其他线程调用这个监视器对象的notify()或notifyAll()方法。
        a.notify(),它只是会随机释放出a监狱的一个线程,不保证放出的是哪个线程!
        a.notfiyAll(),把a监狱中所有线程都释放出来。

4 等待状态的线程会释放对象锁
  当一个线程执行了wait(),那么它会释放出对象锁。即不在占有监视器对象了!

5 被唤醒的线程会在醒来的位置向下运行
  被唤醒的线程,会在wait()方法开始向下运行!

6 线程间通讯小结
使用wait()、notify()、notifyAll()方法可以完成线程间的通讯,可叫它们通讯方法;
只能在同步环境下调用通讯方法;
只能使用监视器对象调用通讯方法;
每个监视器对象都有一个线程监狱:执行a.wait()的线程会被关押到a对象的线程监狱中;
若想释放出a对象的线程监狱中的线程,那么需要调用a.notify()方法,该方法只能保证在a对象的线程监狱中释放出一个线程,但不能保证释放的是哪一个;
还可以使用a.notifyAll()方法释放出a对象的监狱中关押的所有线程。
被wait()了的线程不能自己恢复到就绪状态,只能等待其他线程调用同一监视器对象上的notify()或notifyAll()方法来唤醒。
被wait()了的线程会释放监视器对象的对象锁,这样其他线程就可以进入他占用的同步环境。
被唤醒的线程恢复到了就绪状态,当再次获取监听器对象的锁后会在wait()处向下运行。

后台线程(守护线程)
当只省守护线程时,守护线程会自己结束;
设置一个线程守护:在新状态进行执行,一旦线程运行之后,再设置就无效了。方法:setDaemon(true);
守护线程的子线程默认是守护线程!在?a方法中创建一个线程,如果a方法是由一号线程调用的,那么在a方法中创建的线程就是一号线程的子线程。
1 什么是后台线程
        当有线程在没有结果时,JVM就会一直运行。
        但如果所有的非后台线程,都结束了,那么后台线程会自动自杀!然后JVM结束!

2 什么时候用后台线程
  当某个线程只是为了其他线程服务,而自己没有独立存在的价值。

3 如何设置线程为后台线程
  ?Thread类的setDaemon(true)方法时,那么该线程就是后台线程了。

4 线程运行后是否还可以设置为后台线程
  不行!只有在新状态时才能调用setDaemon()方法,一旦线程已经是start()方法后,就不能再调用setDaemon()了,调用了也没有!

5 后台线程的子线程
  后台线程的子线程都是后台线程!

合并线程(join)

1 什么合并线程
        main() {
           Thread th = …
           th.start();
           th.join();
        }
        主线程和th两个线程。
        th.join() -- 主线程要等待th线程结束。(当前线程等待该线程结束)

线程让步(yield)
  它是Thread类的静态方法
1 yield()方法的作用
  告诉CPU,可以给我断电了,但我不会改变状态!

2 多核CPU下yield()方法的效果
  多核就是有多个CPU,可能让步都让不出去!

3 让步不同与阻塞
  调用了yield()方法,当前线程还是就绪状态!
回复 使用道具 举报
太好了 ,喜欢  谢谢哥们
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马