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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 马年出黑马 中级黑马   /  2014-4-4 00:07  /  980 人查看  /  3 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 马年出黑马 于 2014-4-4 00:09 编辑

1、进程和线程有什么区别?
进程:正在执行的程序,线程是进程中的内容。每个应用程序中至少有一个线程,每一个进程执行都有一个执行顺序,该顺序是一个执行路径或者叫一个控制单元。进程就是用于定义空间标示空间,进程以启动在内存中就分配一块空间。
线程:就是进程中的一个独立的控制单元线程在控制着进程的执行,一个进程中至少有一个线程。所以说进程中包含线程,而线程决定进程的生命周期。


2、多线程存在的意义以及特性
多线程意义:多代码同时运行,提高效率。
特性:随机性,在某一时刻只能有一个线程在运行(多核除外)比如我们用电脑即能听歌,有能看视频,因为CPU在做着高速的切换,以达到看上去同时运行的效果,我们可以形象的把多线程运行行为看做是在抢夺CPU的执行权,谁抢到谁执行。
什么时候使用线程:当某些代码需要被同时执行时就用单独的线程进行封装。


3、创建线程方法之一(继承Thread类)
//创建线程 通过对API文档的查找发现java已经封装好对这类事物操作的对象 即Thread类
//创建线程的第一种方法 继承Thread类 并复写其中的run方法
步骤:1、定义一个类继承Thread
   2、覆写Thread类中的run方法(将自定义的代码存储到在run方法中,让线程运行)
   3、调用线程中的start方法,该方法有两个作用:启动线程和调用线程中run方法
eg:代码示例
  1. class Demo extends Thread
  2. {
  3. public void run()
  4. {
  5. for(int i=0;i<100;i++)
  6. {
  7. System.out.println("第一个线程"+"-----"+i);
  8. }
  9. }
  10. }
  11. class  ThreadDemo1
  12. {
  13. public static void main(String[] args)
  14. {
  15. //建立好一个对象就创建好一个线程
  16. Demo d= new Demo();
  17. //调用start方法 目的是开启线程并调用run方法
  18. d.start();
  19. for(int i=0;i<100;i++)
  20. {
  21. System.out.println("主线程main"+"-----"+i);
  22. }
  23. }
  24. }
  25. //运行结果是互相交错的 因为该程序中有两个线程 main线程和d线程 cup在这两个线程之间做着高速切换的动作
  26. //多个线程在抢劫cpu资源
复制代码



4、 为什么要覆盖run方法?
Thread类中用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,也就是说Thread类中的run方法,是用于存储线程要运行的代码。(就是告诉线程你如果要干活,就干这一块别的不要管就行)。

3 个回复

倒序浏览
6、创建线程方法之一(实现Runnable接口)
定义类声明实现Runnable接口,该类然后实现run方法,之后创建该类的实例,在创建Thread时将该类实例作为参数传递并启动线程。
步骤:1、定义类实现Runnable接口
   2、覆盖Runnable接口中的run方法(将线程要运行的代码存放在run方法中)
   3、通过Thread类建立线程对象
   4、将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
   5、调用Thread类的start()方法,开启线程并调用Runnable接口子类的run方法
小知识:
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为自定义的run方法所属对象时Runnable接口的子类对象,所以要让线程去执行制定对象的run方法,就必 须明确该run方法所属对象。
代码示例:

  1. <p>class Ticket implements Runnable</p><p>{</p><p>private int ticket = 100;</p><p>public void run()</p><p>{</p><p>while(true)</p><p>{</p><p>if(ticket>0)</p><p>{</p><p>System.out.println(Thread.currentThread().getName()+"----"+ ticket--);</p><p>//Thread类中有一个静态方法currentThread()获取当前线程对象getName()线程名字</p><p>}</p><p>}</p><p>}</p><p>}</p><p>class  maiPiao</p><p>{</p><p>public static void main(String[] args) </p><p>{</p><p>//创建Runnable子类对象</p><p>Ticket t = new Ticket();</p><p>//通过Thread类 创建线程对象并且把Runnable接口子类对象作为参数传递给Thread类</p><p>Thread t1 = new Thread(t);</p><p>Thread t2 = new Thread(t);</p><p>Thread t3 = new Thread(t);</p><p>Thread t4 = new Thread(t);</p><p>//通过Thread对象调用start方法 开启线程并执行Runnable子类对象中的run方法</p><p>t1.start();</p><p>t2.start();</p><p>t3.start();</p><p>t4.start();</p><p>
  2. </p><p>}</p><p>}</p>
复制代码



7、定义线程继承和实现的区别?
1、实现方式好处避免了单继承的局限性(建议使用实现方式)
2、继承Thread类线程代码存放在Thread子类run方法中
3、实现Runnable线程代码存放在接口子类的run方法中


回复 使用道具 举报
本帖最后由 马年出黑马 于 2014-4-4 00:14 编辑

8、线程安全问题
8.1 问题怎么产生的
在多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一条线程就参与进来执行,导致共享数据的错误。就比如火车站买票,火车票是共享数据而多个窗口是线程都在同时操作火车票。

8.2 怎么解决问题
思路:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中其他线程不可以参与执行、
Java对多线程安全问题提供了专业的解决方式就是同步代码块
格式:
synchronized(锁对象)
{
需要被同步的代码(正在操作共享数据的代码);
}
对象:对象如同锁,持有锁的线程才可以在同步中执行(火车卫生间),没有持有锁的线程及时有CPU执行权也进不去,因为没有获取锁,锁就相当于一个通行证 只有持有通行证的线程才可以进去。

8.3 同步的前提
1、必须有两个或两个以上的线程
2、必须是多线程使用同一个锁
3、必须保证同步中只能有一个线程

8.4 同步的利与弊
利:解决了多线程的安全问题,保证数据的准确性
弊:多个线程都需要判断锁较为消耗资源

8.5 针对多线程如何找问题
1、明确哪些代码是多线程运行的代码
2、明确共享数据
3、明确多线程运行代码中哪些语句是操作共享数据的

  8.6 同步函数
就是把synchronized关键字作为修饰符放在函数上修饰,该函数就变成了同步函数。
eg:代码示例
public synchronized void add()
{
函数内部代码;
}
同步函数锁的知识点:同步函数需要被对象调用,那么函数都有一个所属对象的引用就是this,所以同步函数使用的锁是this。如果同步函数被静态所修饰后使用的锁是该类的字节码文件即:类名.class 因为静态进内存时内存中没有本类对象,但是一定有该类对应的字节码文件对象,所以被静态修饰的同步函数持有的锁是该类的字节码文件。

8.7单例设计模式(延迟加载 静态修饰同步函数 )
  1. class Single
  2. {
  3. //私有构造函数
  4. private Single(){}
  5. //建立本类对象引用
  6. private static Single s = null;
  7. //提供公共访问方式 返回本类对象
  8. public static Single getSingle()
  9. {
  10. if(s==null)
  11. {
  12. synchronized(Single.class)
  13. {
  14. if(s==null)
  15. {
  16. s= new Single();
  17. }
  18. }
  19. }
  20. return s;
  21. }
  22. }
复制代码






回复 使用道具 举报
8.8死锁
   什么是死锁?
    所谓死锁就是指两个以上的线程互相都有要求使用对方已经占有的资源而导致无法继续运
    行的现象。就是同步中嵌套同步。
   简单的一个死锁程序代码:
  1.   public class DeadLock
  2.    {
  3.     private  static final Object obj1 = new Object();
  4.     private static final Object  obj2 = new Object();
  5.     public class Run1 implements Runnable
  6.     {
  7.      String threadName = Thread.currentThread().getName();
  8.      public void run()
  9.      {
  10.       synchronized(obj1)
  11.       {     
  12.        System.out.println(threadName+"进入同步块obj准备进入obj2");
  13.        synchronized(obj2)
  14.        {
  15.         System.out.println(threadName+"已经进入同步块obj2");
  16.        }
  17.       }
  18.      }
  19.     }
  20.     public class Run2 implements Runnable
  21.     {
  22.      String threadName = Thread.currentThread().getName();
  23.      public void run()
  24.      {
  25.       synchronized(obj2)
  26.       {     
  27.        System.out.println(threadName+"进入同步块obj准备进入obj1");
  28.        synchronized(obj1)
  29.        {
  30.         System.out.println(threadName+"已经进入同步块obj1");
  31.        }
  32.       }
  33.      }
  34.       
  35.     }
  36.     public static void main(String[] args)
  37.     {
  38.      Thread t1 = new Thread(new DeadLock().new Run1());
  39.      Thread t2 = new Thread(new DeadLock().new Run2());
  40.      t1.start();
  41.      t2.start();
  42.     }
  43.    }
复制代码



9、线程之间的通讯
9.1 线程通讯概述
线程间通讯其实就是多个线程在操作同一资源,但操作的动作不同,例如两辆车一辆拉煤一辆送煤。当两个线程有安全问题是需要根据同步的前提进行判断(两个或两个以上的线程,锁必须相同,必须保证同步中只用一个线程在运行)
9.2 等待唤醒机制
wait( )你等会、notify( )你醒醒、notifyAll( )都醒了唤醒线程池中所有的线程,这三个方法全用在同步中,因为要对持有监视器(锁)的线程进行操作,所以要使用在同步中,因为只有同步才具有锁。

9.3为什么操作线程的放大都定义在Object中呢?
因为这些方法在操作同步中线程时都必须要标示他们所操作线程持有的锁。只有同一个锁上被等待的线程可以被同一个锁上的notify()唤醒,不可以对不同锁中的线程进行唤醒。也就是说等待和唤醒机制必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马