1、多线程:一个应用程序有多条执行路径 进程:正在运行的应用程序(是系统进行资源分配和调用的独立单元,每一个进程有它自己的内存空间和系统资源) 线程:进程的执行单元(程序执行的任务),执行路径 单线程:一个应用程序只有一条执行路径 多线程:一个应用程序有多条执行路径(一个程序执行多个任务) 并行和并发: 前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。 后者是物理上同时发生,指在某一个时间点同时运行多个程序。 多进程的意义? 提高CPU的使用率 多线程的意义? 提高应用程序的使用率 2、Java程序的运行原理及JVM的启动是多线程的吗? A:Java命令去启动JVM,JVM会启动一个进程,该进程会启动一个主线程,主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。 B:JVM的启动是多线程的,它最少有两个线程启动,主线程和垃圾回收线程。 3、多线程的实现方案(掌握) A:继承Thread类 1)自定义类MyThread继承Thread类 2)MyThread类里面重写run() run+alt+/ 为什么? 不是类中所有代码都需要被线程执行(方法包含 需要线程执行的代码(比较耗时的代码)),为了区分哪些代码能够被线程执行。 getName(); //获取线程名称(在run()方法中) Thread.currentThread().getName(); //通用获取线程名称 3)创建对象 MyThread mt = new MyThread(); 4)启动线程 mt.start(); 注意:重复调用run方法是单线程 run()和start()的区别? run():仅仅是封装被线程执行的代码,直接调用是普通方法 start():首先启动了线程,然后再由jvm去调用该线程的方法 IllegalThreadStasteException:指非法线程状态异常 why? 两个start()相当于是mt线程被调用了两次。而不是两个线程启动。 解决:创建两个线程对象 MyThread mt2 = new MyThread(); mt.setName("baron"); //设置线程名称 mt.start(); mt2.start(); B:实现Runnable接口(常用) 1)自定义类MyRunnable类实现Runnable接口 2)重写run()方法 3)创建MyRunnable类对象 4)创建Thread类对象,并把3)步骤的对象作为构造参数传递 C:有了方式一为什么还有方式二? 解决了Java单继承带来的局限性 适合多个相同程序的代码去处理一个资源的情况,把线程同程序的代码,数据有分离。较好的体现了面向对象的设计思想。 4、线程的调度和优先级问题 假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,线程只 有得到 CPU时间片,也就是使用权,才可以执行指令。 A:线程的两种调度模型 a:分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片 b:抢占式调度 (Java采用的是该调度方式):优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。 B:获取和设置线程优先级 a:默认是5 b:范围是1-10 c:获取线程对象优先级 public final int getPriority() mt.getPriority(); d:设置对象优先级 mt.setPriority(); IllegalArgumentException:非法参数异常 线程优先级高仅仅表示线程获取的CPU时间片的几率高,但要在次数比较多,或者多次运行的时候才能看到比较好的效果 5、线程的控制(常见方法) A:休眠线程 public static void sleep(long millis) Thread.sleep(1000); //在run()或main()方法中休息一秒 B:加入线程 public final void join() //等待线程终止(意思是其它线程等待我执行完毕后才可执行) mt.join(); C:礼让线程 public static void yield():暂停当前正在执行的线程对象,并执行其它线程。 Thread.yield(); //在run()方法中让多个线程的执行更加和谐,但不能靠它保证一人一次。 D:后台线程 public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程,当正在运行的线程是守护线程时,Java虚拟机退出,该方法必须在启动线程前调用。 mt.setDaemon(true); mt2.setDaemon(true); E:终止线程(掌握) public final void stop():过时,但是还可以用 mt.stop(); public void interrupt():终止线程,把线程状态终止,并抛出InterruptedException mt.interrupt(); 6、线程的生命周期(参照 线程生命周期图解.bmp) A:新建:创建线程对象 B:就绪:有执行资格,无执行权限 C:运行:有执行资格,有执行权限 D:阻塞:由于一些操作让线程处于该状态,没有执行资格,没有执行权限。一些操作可将其激活,激活后处于就绪状态。 E:死亡:线程对象变成垃圾,等待被回收 7、电影院卖票程序的实现 某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。 A:继承Thread类 B:实现Runnable接口 8、电影院卖票程序出问题 A:为了更符合真实的场景,加入了休眠100毫秒。 B:卖票问题 a:同票多次 b:负数票 9、多线程安全问题的原因(也是我们以后判断一个程序是否有线程安全问题的依据) A:是否有多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 10、同步解决线程安全问题 A:同步代码块 synchronized(对象) { 需要被同步的代码; } 这里的锁对象可以是任意对象。 B:同步方法 把同步加在方法上。 这里的锁对象是this C:静态同步方法 把同步加在方法上。 这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象) 例子: @Override public void run() { while (true) { if(x%2==0){ synchronized (SellTicket.class) { //例子B换成this if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票 "); } } }else { sellTicket(); } x++; } } private static synchronized void sellTicket() { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票 "); } } } D:同步的前提 多个线程 多个线程使用的是同一个锁对象 E:同步的弊端: 当线程较多时,因为每个线程都会去判断同步上的锁,很耗资源,无形中降低程序运行效率 11、回顾以前的线程安全的类 A:StringBuffer B:Vector C:Hashtable D:如何把一个线程不安全的集合类变成一个线程安全的集合类 用Collections工具类的方法即可。 // Vector是线程安全的时候才去考虑使用的,但是我还说过即使要安全,我也不用你 // 那么到底用谁呢? // public static XXXX synchronizedXXXX(XXXX XXXX) List list1 = new ArrayList();// 线程不安全 List list2 = Collections.synchronizedList(new ArrayList()); // 线程安全