黑马程序员--java基础--多线程

来源:互联网 发布:软件系统业务流程图 编辑:程序博客网 时间:2024/06/14 07:26

一、多线程简介 

1:要想说线程,首先必须得聊聊进程,因为线程是依赖于进程存在的。

2:那么,什么是进程?

  进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。

3:多进程有什么意义呢?

  单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏(游戏进程),一边听音乐(音乐进程),所以我们常见的操作系统都是多进程操作系统。比如:WindowsMacLinux等,能在同一个时间段内执行多个任务。

  对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。

  因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,且切换速度很快,所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。

  多进程的作用不是提高执行速度,而是提高CPU的使用率。

4:那么什么又是线程呢?

  在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。线程是程序中单个顺序的控制流,是程序使用CPU的基本单位。

5:多线程有什么意义呢?

  多线程的作用不是提高执行速度,而是为了提高应用程序的使用率。

  而多线程却给了我们一个错觉:让我们认为多个线程是并发执行的。其实不是。

  因为多个线程共享同一个进程的资源(堆内存和方法区),但是栈内存是独立的,一个线程一个栈。所以他们仍然是在抢CPU的资源执行。一个时间点上只有能有一个线程执行。而且谁抢到,这个不一定,所以,造成了线程运行的随机性。

二、创建任务和线程 
1. 实现方法一 
  首先,我们为任务定义一个类,任务类必须实现Runnable接口,它只包含一个run方法,需要实现这个方法来告诉系统线程将如何运行。任务必须在线程中执行。Thread类包括创建线程的构造方法以及控制线程的很多有用的方法。 
  示例如下: 
Java代码  收藏代码
  1. package test;  
  2. public class Test  
  3.     public static void main(String[] args)  
  4.         //创建任务  
  5.         PrintString print new PrintString("jiang");  
  6.         //创建任务的线程  
  7.         Thread thread new Thread(print);  
  8.         //调用start告诉java虚拟机线程已准备就绪  
  9.         thread.start();   
  10.      
  11.  
  12.   
  13. //定义任务类,实现Runnable接口,重载run方法  
  14. class PrintString implements Runnable{  
  15.     private String strToPrint;  
  16.     public PrintString(String str){  
  17.         this.strToPrint str;  
  18.      
  19.     public void run()  
  20.         System.out.println(this.strToPrint);  
  21.      
  22.  

  在调用start告诉java虚拟机线程已准备就绪后,Java虚拟机通过调用任务的run()方法执行任务。 

2. 实现方法二 
  因为Thread类实现了Runnable接口,故可以定义一个Thread类的扩展类,并且实现run方法。它将任务和运行任务的机制混在了一起,故不推荐使用这种方法。 
Java代码  收藏代码
  1. package test;  
  2. public class Test  
  3.     public static void main(String[] args)  
  4.         TestThread tt new TestThread("qin");  
  5.         tt.start();  
  6.      
  7.  
  8. //直接定义Thread类的扩展类,实现run方法  
  9. class TestThread extends Thread{  
  10.     private String strToPrint;  
  11.     public TestThread(String str){  
  12.         this.strToPrint str;  
  13.      
  14.     public void run()  
  15.         System.out.println(this.strToPrint);  
  16.      
  17.  


三、Thread类 
  Thread类包含为任务而创建的线程的构造方法,以及控制线程的方法。 
    Thread() //创建一个空线程 
    Thread(Runnable task) //为指定任务创建一个线程 
    public void start() //启动线程使任务的run()被JVM调用 
    public boolean isAlive() //测试线程当前是否正在运行,创建和结束状态时返回false,就绪、阻塞、运行状态时返回true。 
    public int getPriority() //得到线程的优先级,默认线程继承生成它的线程的优先级 
    public void setPriority(int p) //设置线程的优先级p(范围从1到10) 
    public void join() //使此线程等待另一个线程的结束,在另一个线程里面调用 
    public void sleep(long mi) //使线程睡眠指定的毫秒数 
    public void yield() //使线程暂停并允许执行其他线程,为其他线程临时让出SPU时间 
    public void interrupt() //中断线程,就绪或运行状态时给他设置一个中断标志,阻塞状态时它将被唤醒进入就绪状态并抛出InterruptedException异常。 
  Thread类还包含方法stop()、suspend()、resume(),由于普遍认为这些方法具有内在有不安全因素,所以不提倡使用这些方法。为替代stop()的使用可以通过给Thread变量赋值null来表明它被停止。 
  Thread类有int型常量MIN_PRIORITY、NORM_PRIORITY、MAX_PRIORITY,分别代表1,5,10。Java虚拟机总是选择当前优先级最高的可运行线程。如果所有可运行线程具有相同的优先级,那将会用循环队列给它们分配相同的CPU份额。但我在测试时发现并没有绝对的按优先级来调度,测试如下: 
Java代码  收藏代码
  1. public class TestRunnable1 implements Runnable  
  2.   @Override  
  3.   public void run()  
  4.     for (int 1<= 1000i++)  
  5.       System.out.println("A " i);  
  6.      
  7.    
  8.  
  9.   
  10. public class TestRunnable2 implements Runnable  
  11.   @Override  
  12.     public void run()  
  13.       for (int 1<= 1000i++)  
  14.         System.out.println("B " i);  
  15.        
  16.      
  17.  
  18.   
  19. public class TestThead  
  20.   public static void main(String[] args)  
  21.     Thread t1 new Thread(new TestRunnable1());  
  22.     Thread t2 new Thread(new TestRunnable2());  
  23.     t1.setPriority(6);  
  24.     t2.setPriority(Thread.MAX_PRIORITY);  
  25.     System.out.println("A Priority " t1.getPriority());  
  26.     System.out.println("B Priority " t2.getPriority());  
  27.     t1.start();  
  28.     t2.start();  
  29.    
  30.  

  虽然绝大部分t2都会在t1前面输出,但t2输 出中总会插几条t1的输出。 

四、线程的状态 
  线程可以是以下五种状态之一: 
    1)新建:新建一个线程时。 
    2)就绪:调用线程的start()方法启动线程后、CPU时间用完、调用线程的yield()方法、休眠时间到。 
    3)运行:获得CPU时间开始执行。 
    4)阻塞:调用了join()、sleep()、wait()方法。 
    5)结束:执行完run()方法这个线程就被结束。 

五、线程同步 
  共享资源在被多个线程同时访问时,可能遭到破坏。为避免竞争状态,应该防止多个线程同时进入程序的某一特定部分(临界区)。 
1. synchronized 
  我们可以使用synchronized关键字来同步方法或语句块,以便一次只在一个线程可以访问。如下: 

Java代码  收藏代码
  1. //测试同步方法  
  2. public synchronized void printStri(){  
  3.   for(int i=0i<100;i++){  
  4.     System.out.println(i);  
  5.    
  6.  
  7.   
  8. //测试同步块,同步语句不仅可以对this对象加锁,而且可用于对任务对象加锁。  
  9. public void printStri(){  
  10.   synchronized(this){  
  11.     for(int i=0i<100;i++){  
  12.       System.out.println(i);  
  13.      
  14.    
  15.  


2. 利用加锁同步 
  一个锁是一个Lock接口的实例,它定义了加锁和释放锁的方法。操作如下: 
    public void lock() //加锁 
    public void unlock() //释放锁 
    public Condition newCondition() //返回到绑定到Lock实例的新的Condition实例 

  ReentrantLock类是Lock接口的一个具体实现,它创建一个相互排斥的锁,如下: 
    ReentrantLock() //等价于ReentrantLock(false) 
    ReentrantLock(boolean f) //创建一个具有公平策略的锁,true时等待时间最长的线程将获得锁。false时没有特定的顺序。 
  实例如下: 
Java代码  收藏代码
  1. private static class PrintStr{  
  2.   //创建一个锁  
  3.   private static Lock lock new ReentrantLock();  
  4.   //测试同步方法  
  5.   public void printStr(){  
  6.     //加锁  
  7.     lock.lock();  
  8.     try 
  9.       for(int i=0i<100;i++){  
  10.         System.out.println(i);  
  11.        
  12.     catch(Exception e){  
  13.       e.printStackTrace();  
  14.     }finally  
  15.       //解锁  
  16.       lock.unlock();  
  17.      
  18.    
  19.  


六、线程通信 
  通过保证在临界区上多个线程的相互排斥,线程同步完全可以避免竞争状态的发生,但是有些时候还需要线程之间的协作。使用条件可以便于线程间的通信,条件是通过调用Lock对象的newCondition()方法而创建的对象。一旦创建了条件,就可以使用await()、signal()、signlAll()方法来实现线程之间的相互通信。Condition具体操作如下: 
    public void await() //当前线程等待直到发生某个条件,同普通对象的wait()方法 
    public void signal() //唤醒一个等待线程,同普通对象的notify()方法 
    public  Condition signalAll() //唤醒所有等待的线程,同普通对象的nofityAll()方法 
  示例: 
Java代码  收藏代码
  1. package test;  
  2. import java.util.concurrent.ExecutorService;  
  3. import java.util.concurrent.Executors;  
  4. import java.util.concurrent.locks.Condition;  
  5. import java.util.concurrent.locks.Lock;  
  6. import java.util.concurrent.locks.ReentrantLock;  
  7.   
  8. public class TestCondition  
  9.   private static Account account new Account();  
  10.   public static void main(String[] args)  
  11.     ExecutorService executorService Executors.newFixedThreadPool(2);  
  12.     executorService.execute(new DepositTask());  
  13.     executorService.execute(new WithdrawTask());  
  14.     executorService.shutdown();  
  15.    
  16.   
  17.     
  18.   public static class DepositTask implements Runnable  
  19.     @Override  
  20.     public void run()  
  21.       try  
  22.         account.deposit((int(Math.random() 101);  
  23.         Thread.sleep(1000);  
  24.       catch (InterruptedException e)  
  25.         // TODO: handle exception  
  26.        
  27.      
  28.    
  29.   
  30.     
  31.   public static class WithdrawTask implements Runnable  
  32.     @Override  
  33.     public void run()  
  34.       while (true 
  35.         account.withdraw((int(Math.random() 101);  
  36.        
  37.      
  38.    
  39.   
  40.     
  41.   public static class Account  
  42.     private static Lock lock new ReentrantLock();  
  43.     private static Condition newDeposit lock.newCondition();  
  44.     private int balance 0// 账户金额  
  45.   
  46.     public int getBalance()  
  47.       return balance;  
  48.      
  49.   
  50.     public void withdraw(int amount)  
  51.       lock.lock();  
  52.       try  
  53.         while (balance amount)  
  54.           System.out.println("余额不足,等待充值");  
  55.           newDeposit.await();  
  56.          
  57.         balance -= amount;  
  58.         System.out.println("取款:" amount "余额:" balance);  
  59.       catch (InterruptedException e)  
  60.         // TODO: handle exception  
  61.       finally  
  62.         lock.unlock();  
  63.        
  64.      
  65.   
  66.     public void deposit(int amount)  
  67.       lock.lock();  
  68.       try  
  69.         while (balance 10 
  70.           balance += amount;  
  71.           System.out.println("充值:" amount "余额:" balance);  
  72.           newDeposit.signalAll();  
  73.          
  74.       finally  
  75.         lock.unlock();  
  76.        
  77.      
  78.    
  79.  

  警告:为了使用条件,必须首先获取锁。一旦线程调用条件上的await(),线程就进入等待状态,等待恢复的信号。如果忘记对状态调用signal()或者signalAll(),那么线程就永远的等待下去。 
  线程通信是使用对象的内置监视器编程实现的。 
0 0
原创粉丝点击