黑马程序员——java基础_多线程
2015-08-15 19:33
399 查看
[code] 1:多线程(理解) (1)多线程:一个应用程序有多条执行路径 进程:正在执行的应用程序 线程:进程的执行单元,执行路径 单线程:一个应用程序只有一条执行路径 多线程:一个应用程序有多条执行路径 多进程的意义? 提高CPU的使用率 多线程的意义? 提高应用程序的使用率 (2)Java程序的运行原理及JVM的启动是多线程的吗? A:Java命令去启动JVM,JVM会启动一个进程,该进程会启动一个主线程。 B:JVM的启动是多线程的,因为它最低有两个线程启动了,主线程和垃圾回收线程。 (3)多线程的实现方案(自己补齐步骤及代码 掌握) 方式1:继承Thread类 A:自定义类MyThread继承Thread类 B:在MyThread类中重写run方法 C:创建MyThread类的对象 D:启动线程对象 方式2:实现Runnable接口 A:自定义类MyRunnable实现Runnable接口 B:在MyRunnable里面重写run() C:创建MyRunnable对象 D:创建Thread类的对象,并把C步骤的对象作为构造参数传递 问题: a:为什么要重写run方法? run()里面封装的是被线程执行的代码 b:启动线程对象用的是哪个方法? start() c:run()和start()方法的区别? run()直接嚼用仅仅是普通方法, start()先启动线程,再由JVM调用run()方法 (4)线程的调度和优先级问题 A:线程的调度 a:分时调度 b:抢占式调度 (Java采用的是该调度方式) B:获取和设置线程优先级 a:默认是5 b:范围是1-10 (5)线程的控制(常见方法) A:休眠线程 sleep B:加入线程 join C:礼让线程 yield D:后台线程 setDaemon(该方法必须在启动线程前调用) E:终止线程(掌握) interruput (6)线程的生命周期(参照下面线程生命周期图解.bmp) A:新建 B:就绪 C:运行 D:阻塞 E:死亡 (7)多线程安全问题的原因(也是我们以后判断一个程序是否有线程安全问题的依据) A:是否有多线程环境 B:是否有共享数据 C:是否有多条语句操作共享数据 (8)同步解决线程安全问题 A:同步代码块 synchronized(对象) { 需要被同步的代码; } 这里的锁对象可以是任意对象。 B:同步方法 把同步加在方法上。 这里的锁对象是this C:静态同步方法 把同步加在方法上。 这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象)
线程和进程的概述
[code]1:要想了解多线程,必须先了解线程,而要想了解线程,必须先了解进程,因为线程 是依赖于进程而存在。 2:什么是进程? 通过任务管理器我们就看到了进程的存在。 而通过观察,我们发现只有运行的程序才会出现进程。 进程:就是正在运行的程序。 进程是系统进行资源分配和调用的独立单位。每一个进程都有它自己的内存空间和系统资源。 3:多进程有什么意义呢? 单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。 举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。 也就是说现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务。 并且呢,可以提高CPU的使用率。 问题: 一边玩游戏,一边听音乐是同时进行的吗? 不是。因为单CPU在某一个时间点上只能做一件事情。 而我们在玩游戏,或者听音乐的时候,是CPU在做着程序间的高效切换让 我们觉得是同时进行的。 4:什么是线程呢? 在同一个进程内又可以执行多个任务,而这每一个任务我就可以看出是一个线程。 线程:是程序的执行单元,执行路径。是程序使用CPU的最基本单位。 单线程:如果程序只有一条执行路径。 多线程:如果程序有多条执行路径。 5:多线程有什么意义呢? 多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。 程序的执行其实都是在抢CPU的资源,CPU的执行权。 多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会 有更高的几率抢到CPU的执行权。
多线程两种方式的图解比较及区别:
线程生命周期图解:
下面是案例与代码体现:
1,Runnable方式实现售票,
2,Lock机制来体现售票
3,线程组:ThreadGroup,
4,线程池:Callable
5,定时器:Timer
6,定时器案例:在指定时间删除某目录
Thread方式实现售票案例
[code]/* * 某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。 * 继承Thread类来实现。 */ public class SellTicketDemo { public static void main(String[] args) { //创建三个线程共同卖票 SellTicket s1 = new SellTicket(); SellTicket s2 = new SellTicket(); SellTicket s3 = new SellTicket(); //给线程起名字 s1.setName("窗口1"); s2.setName("窗口2"); s3.setName("窗口3"); //开启线程 s1.start(); s2.start(); s3.start(); } } public class SellTicket extends Thread { //定义一百张票, //为了让多个线程对象共享这100张票,我们其实应该用静态修饰 private static int tickets = 100; public void run() { // 定义100张票 // 每个线程进来都会走这里,这样的话,每个线程对象相当于买的是自己的那100张票,这不合理,所以应该定义到外面 // int tickets = 100; //循环一直有票 while(true) { if(tickets>0) { System.out.println( getName() + "正在售出第:"+(tickets--)+"张票"); } } } }
Runnable方式实现:
[code]/* * 实现Runnable接口的方式实现 * * 通过加入延迟后,就产生了连个问题: * A:相同的票卖了多次 * CPU的一次操作必须是原子性的 * B:出现了负数票 * 随机性和延迟导致的,我们使用同步锁解决 */ public class SellTicketDemo { public static void main(String[] args) { //创建资源对象 SellTicket st = new SellTicket(); //创建三个线程对象 Thread t1 = new Thread(st,"窗口1"); Thread t2 = new Thread(st,"窗口2"); Thread t3 = new Thread(st,"窗口3"); //启动线程 t1.start(); t2.start(); t3.start(); } } public class SellTicket implements Runnable { //定义100张票 private int tickets = 100; //创建一把锁对象 Object obj = new Object(); public void run() { while(true) { // t1,t2,t3都能走到这里 // 假设t1抢到CPU的执行权,t1就要进来 // 假设t2抢到CPU的执行权,t2就要进来,发现门是关着的,进不去。所以就等着。 // 门(开,关) synchronized(obj){// 发现这里的代码将来是会被锁上的,所以t1进来后,就锁了。(关) if(tickets > 0) { try { Thread.sleep(100); // t1就睡眠了 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第:"+(tickets--)+"张票"); //窗口1正在出售第100张票 } }//t1就出来可,然后就开门。(开) } } }
我们继续看代码使用JDK1.5 Lock机制来体现:
[code]/* * 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁, * 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。 * * Lock: * void lock(): 获取锁。 * void unlock():释放锁。 * ReentrantLock是Lock的实现类. */ public class SellTicketDemo { public static void main(String[] args) { //创建资源对象 SellTicket st = new SellTicket(); //创建三个线程窗口 Thread t1 = new Thread(st,"窗口1"); Thread t2 = new Thread(st,"窗口2"); Thread t3 = new Thread(st,"窗口3"); //启动线程 t1.start(); t2.start(); t3.start(); } } public class SellTicket implements Runnable{ //定义票 private int tickets = 100; //定义锁对象 private Lock lock = new ReentrantLock(); @Override public void run() { while(true){ try{ //加锁 lock.lock(); if(tickets>0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在售第"+(tickets--)+"张票"); } }finally{ //释放锁 lock.unlock(); } } } }
线程组:ThreadGroup
[code]/* * 线程组: 把多个线程组合到一起。 * 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。 */ public class ThreadGroupDemo { public static void main(String[] args) { //method1(); // 我们如何修改线程所在的组呢? // 创建一个线程组 // 创建其他线程的时候,把其他线程的组指定为我们自己新建线程组 method2(); } private static void method2(){ // ThreadGroup(String name) ThreadGroup tg = new ThreadGroup("这是一个新的组"); MyRunnable my = new MyRunnable(); // Thread(ThreadGroup group, Runnable target, String name) 在设置线程的时候同时设定组名 Thread t1 = new Thread(tg,my,"林青霞"); Thread t2 = new Thread(tg,my,"风清扬"); System.out.println(t1.getThreadGroup().getName()); System.out.println(t2.getThreadGroup().getName()); //通过组名设置后台线程,表示该组的线程都是后台线程,守护线程 tg.setDaemon(true); } private static void method1() { MyRunnable my = new MyRunnable(); Thread t1 = new Thread(my, "林青霞"); Thread t2 = new Thread(my, "刘意"); // 我不知道他们属于那个线程组,我想知道,怎么办 // 线程类里面的方法:public final ThreadGroup getThreadGroup() ThreadGroup tg1 = t1.getThreadGroup(); ThreadGroup tg2 = t2.getThreadGroup(); // 线程组里面的方法:public final String getName() String name1 = tg1.getName(); String name2 = tg2.getName(); System.out.println(name1); System.out.println(name2); // 通过结果我们知道了:线程默认情况下属于main线程组 // 通过下面的测试,你应该能够看到,默任情况下,所有的线程都属于同一个组 System.out.println(Thread.currentThread().getThreadGroup().getName()); } }
线程池:Callable
[code]/* 线程池的好处:线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。 * * 多线程实现的方式3: * A:创建一个线程池对象,控制要创建几个线程对象。 * public static ExecutorService newFixedThreadPool(int nThreads) * B:这种线程池的线程可以执行: * 可以执行Runnable对象或者Callable对象代表的线程 * 做一个类实现Runnable接口。 * C:调用如下方法即可 * Future<?> submit(Runnable task) * <T> Future<T> submit(Callable<T> task) * D:我就要结束,可以吗? * 可以。 * */ public class CallableDemo { public static void main(String[] args) throws InterruptedException, ExecutionException { //创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。 ExecutorService pool = Executors.newFixedThreadPool(2); //可以执行Runnable对象或者Callable对象代表的线程 Future<Integer> f1 = pool.submit(new MyCallable(100)); Future<Integer> f2 = pool.submit(new MyCallable(200)); //V get() ,获得线程执行的结果,返回值是Integer类型 Integer i1 = f1.get(); Integer i2 = f2.get(); System.out.println(i1); System.out.println(i2); //结束 pool.shutdown(); } } //Callable:是带泛型的接口。 //这里指定的泛型其实是call()方法的返回值类型 //线程求和案例 public class MyCallable implements Callable<Integer>{ private int number; public MyCallable(int number){ this.number = number; } public Integer call() throws Exception{ int sum=0; for(int x=0;x<=number;x++){ sum+=x; } return sum; } }
定时器:Timer
[code]/* * 定时器:可以让我们在指定的时间做某件事情,还可以重复的做某件事情。 * 依赖Timer和TimerTask这两个类: * Timer:定时 * public Timer() * public void schedule(TimerTask task,long delay)//安排在指定延迟后执行指定的任务 * public void schedule(TimerTask task,long delay,long period)//安排指定的任务从指定的延迟后开始进行重复的固定延迟执行 * public void cancel() * TimerTask:任务 */ public class TimerDemo { public static void main(String[] args) { // 创建定时器对象 Timer t = new Timer(); // 3秒后执行爆炸任务 // t.schedule(new MyTask(), 3000); //结束任务 t.schedule(new MyTask(t), 3000); // 3秒后执行爆炸任务第一次,如果不成功,每隔2秒再继续炸 t.schedule(new MyTask(), 3000, 2000); } } // 做一个任务 class MyTask extends TimerTask { private Timer t; public MyTask(){} public MyTask(Timer t){ this.t = t; } @Override public void run() { System.out.println("beng,爆炸了"); //t.cancel();//连续爆炸时,不允许结束 } }
定时器案例:在指定时间删除某目录
[code]/* * 需求:在指定的时间删除我们的指定目标,这里我们删除项目src根目录下的demo文件 * */ //做一个删除文件的任务 class DeleteFolder extends TimerTask{ @Override public void run() { File srcFolder = new File("demo"); deleteFolder(srcFolder); } //递归删除目录方法 private void deleteFolder(File srcFolder) { File[] fileArray = srcFolder.listFiles(); if(fileArray!=null){ for(File file : fileArray){ if(file.isDirectory()){ deleteFolder(file); }else{ System.out.println(file.getName()+":"+ file.delete()); } } System.out.println(srcFolder.getName() +":"+srcFolder.delete()); } } } public class TimerTest { public static void main(String[] args) throws ParseException { //创建一个定时器 Timer t = new Timer(); //自定义一个字符串时间,并解析 String s = "2015-8-15 16:44:00"; SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date d = sdf.parse(s); //执行任务 t.schedule(new DeleteFolder(), d);//此刻注意参数必须是Date类型传递 } }
相关文章推荐
- 十道海量数据处理面试题与十个方法大总结
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 程序员如何快速准备面试中的算法
- 黑马程序员——java基础_正则表达式
- ZooKeeper程序员指南 (
- 一个程序员的自白:我为什么写博客