多线程

一、多线程  
     
     程序是静止的,程序必须在内存中运行,在内存中运行的程序就是一个进程。


     进程(Process)
          a.独立性 
      进程都拥有自己独立的运行内存,进程与进程之间是相互独立的,
      一般来说,进程与进程之间通信是比较难的。
      进程的创建开销是比较大的。
 
 b.动态性 
      程序是静止的,运行中的程序才是动态的,所以进程是动态的,
      进程拥有自己的生命周期。
 
 c.并发性 
      加入系统只有一个CPU
      那么同一个时间片实际上内存中只有一个程序在运行
      CPU会分时轮询处理内存中的每个进程,因为处理的速度很快
      给用户的感觉似乎这些进程都在同时运行,这就是并发。
               
      如果希望操作系统中同一时刻有多个进程并行处理,那就需要多核CPU
      有几个CPU就可以同时运行几个程序。
     
     线程(Thread) 
  一个进程包含了多个线程。
  线程的优势:1.线程基本不占用内存空间,就在进程中运行,创建开销低。
               2.线程也可以参与并发,可以提高进程的运行效率。
 所以多线程的执行会出现随机性。
3.线程之间不仅可以独立,而且线程与线程之间的通信也十分方便
 因为多个线程在同一个进程中。
4.每个线程出现了异常并不会影响其他的线程的运行。
5.java对多线程的支持非常好。


  所以多线程是很有必要的。
  多线程:可以为一个进程创建多个线程,可以为程序的一个资源分配多个线程来处理,可以提高运行的效率。
             
二,多线程的创建 
    
    进程拥有一个主线程可认为就是进程自己, 其他线程就是子线程


    a.为一个进程创建多个线程。有三种方式 
       
       1.继承Thread类创建一个子线程 
           myThread.setName("李刚1"); // 给线程取一个名字 
  /** 拿到当前线程的名称 */
  String name = Thread.currentThread().getName();
           
  优点:写法简单,其他都是缺点。
  缺点:线程已经继承一个类,再不能继承其他类了。


       2.实现Runnable接口创建一个子线程  
           优点:可以继承其他类,多个Thread对象可以使用同一个target来处理。
       
       3.实现Callable接口创建一个子线程。
           优点:可以继承其他类,多个Thread对象可以使用同一个target来处理。
         线程可以有执行结果,线程可以把异常往外抛出去 。


三, 线程的生命周期 
    
    start方法,开启线程,线程不能直接调用run方法,否则当成普通方法来执行。
                
// 这几个线程控制方法都过时了,因为他们会引起线程的死锁。
          myThread.resume();
myThread.destroy();
myThread.suspend();
        
    新建 线程类对象的创建 new Thread()
    就绪 线程对象调用了start()方法后就变成了就绪状态
    运行 就绪状态的线程得到了CPU分时就开始运行
    阻塞 sleep线程,io阻塞,等待同步锁,等待通知,线程挂起suspend();
    死亡 线程的run方法正常执行完成,线程出现了一异常,线程被stop()了。


四, 线程控制,可以通过程序控制多线程的执行(相对的)
     
     a.join线程
         每次运行到join的时候,当前线程应该让调用join的线程先运行以后,自己再接着执行。
     
     b.sleep睡眠线程
         Thread.sleep(毫秒数) 
让当前所在的线程睡眠一段时间,时间到了以后变成就绪状态等待CPU调度继续执行。
     
     c.后台线程
        (Daemon Thread) - 精灵线程、守护线程。
         后台线程有个特征: 如果所有前台线程都死了,它就会自动立即死亡!
注意:如果所有的前台线程先执行完毕了,那么即使后台线程没有执行完也应该立即死亡。
      如果后台线程先执行完毕了,那么后台线程实际上并没有死亡,他要等其他线程都执行完再立即死亡。
         
如何产生一个后台线程呢?
         A。自动方式:后台线程启动的线程本身就是后台线程。
         B。手动方式。调用线程的setDaemon(true)即可。
         
典型地:JDK的GC线程,就是后台线程。
     
     d.yield线程,让步线程 
 yield是让当前线程让出CPU,自己变成了就绪状态
 yield很可能在让出CPU后立即又得到了CPU


     e.线程优先级  
          可以为每个线程设置一个优先级,优先级高的线程在竞争CPU的时候会获得更多的执行机会。
 Thread:
 public final static int MIN_PRIORITY = 1;  // 最低优先级
          public final static int NORM_PRIORITY = 5; // 默认优先级 
          public final static int MAX_PRIORITY = 10; // 最高优先级
          myThread1.setPriority(Thread.MAX_PRIORITY); // 10


五,线程安全与线程同步  
        
      多个线程在访问同一个资源(竞争资源,共享资源)的时候会出现线程安全问题。
                 
10000 
              /        \
           (你) 线程   (老婆)线程 
        10000>=10000   10000>=10000
           true ->       true -> 
   0            -10000
     
      临界区:多条线程访问、修改竞争资源的代码区,就被称为“临界区”。
      解决线程安全的关键:保证任意时刻,最多只有一条线程能进入临界区。
      线程同步是为了解决线程安全问题


     为了保证线程同步,一共有3种方式:
       a. 同步代码块。
           synchronized(acct) {
               // 同步代码块
           }
           synchronized后面括号里的对象叫“同步监视器、monitor”。
           任何线程进入“同步代码块”之前, 必须先对“同步监视器、monitor”加锁(自动完成)
           同步会导致性能下降。
 
           虽然Java的语法,可用任何对象作为同步监视器,但一定要记住:
  实际上只能选择“竞争资源”作为同步监视器。
           当你正确地选择了“竞争资源”作为同步监视器之后,执行流程如下:


           对“竞争资源”加锁  → 进入临界区、访问修改“竞争资源” →  对“竞争资源”解锁
            ——这是一个通用的设计哲学:
           对共享资源加锁 → 修改 → 修改完成后,解锁。其他线程再次进来。


      b.同步方法
          
 被synchronized修饰的方法,就是同步方法。


          对于同步方法而言,如果是实例方法, 使用this作为同步监视器;
                            如果是类方法,使用该类本身作为同步监视器。
          通常来说,应该在“竞争资源”类的里面定义同步方法。


          同步方法、同步代码块的逻辑是完全相同的,区别是同步代码块需要显式指定“竞争资源”作为同步监视器。
          同步方法则直接定义定义“竞争资源”的实现类中,此时this(竞争资源)作为同步监视器。


          同步方法、同步代码块的解锁,也是自动完成——只要程序离开synchronized修饰的代码块、方法,
          此处的离开包括正常结束、因为异常退出,系统会自动释放对同步监视器的锁定。


      c. 同步锁(JDK 1.5才有)
          显式创建Lock、也会显式看到加锁、释放锁。
          Lock是一个接口,该接口提供一个ReentrantLock(可重入锁)实现类。
          做法:
         (1)将显式锁定义成竞争资源的实例变量,并用final修饰(保证锁不会被替换)
              务必保证:每个竞争资源有唯一的、不可替换的锁。


          (2) 程序要修改竞争资源之前,先调用显式锁的lock方法执行锁定;
                       修改完成之后,在finally块中调用unlock方法释放锁定。


六,线程通信  


   生产者和消费者问题 
   生产者生成商品,消费者消费商品
   生产者和消费者必须解决:1.生产的商品不要过剩. 2.消费的商品不能缺货。


   生产者生成商品,通知消费者消费 
   消费消费商品,通知生产者生产。




   线程通信: 首先保证线程安全,就是要线程同步,这是强制性的。


   家庭作业: 购票系统  100张票 ,空座位,正在使用的坐位,有很多的人买票,也有很多人下站
               
   a.线程通信的方式1 
       通过 this.notifyAll()来唤醒所有等待的线程。
       通过 this.wait()来暂停自己的线程。
       先应该唤醒别人才可以停止自己。


    b.线程lock锁实现线程通信
      // 定义一个线程锁 
private final ReentrantLock lock = new ReentrantLock();
// 通过锁得到一个线程通信对象  
private final Condition cont =  lock.newCondition();


cont.signalAll(); // 通知所有的爸爸,赶紧存钱
cont.await();  // 自己等待


加锁和解锁动作一定要执行。


       
   
      
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值