标签:
一、进程
1、这是操作系统中的概念,每个独立运行的程序就是一个进程
2、一个操作系统可以维护多个进程的同时运行,统一分配系统资源
3、一个操作系统可以维护多个进程,这个叫做:支持多进程。多进程的意义:
A、可以充分利用CPU的资源
B、为客户的使用提供了很好的支持,客户可以同时启动多个软件
二、线程
1、一个进程内部的一些代码块,可以以独立于此进程的方式单独运行。它可以与此进程同时竞争系统资源,这些在进程内部以独立的方式运行的代码就叫做线程。
2、意义:
A、可以提高程序的运行效率
B、可以提高CPU的利用率
三、多进程和多线程
1、多进程:
相对于操作系统,可以同时运行多个应用程序
2、多线程
一个主进程可以开出多个线程单独运行
3、单线程
只有一条执行路径
四、并行和并发
1、并行:在某个时间段内,两个线程同时运行
2、并发:在某个时间点上,两个线程同时运行。尤其值多个线程同时访问同一资源时
五、java中多线程的创建和使用
1、创建子线程
1/1 方式一:继承Thread类
①线程在类库中被封装为Thread类,Thread类就是程序中执行的线程
②创建步骤
(1)创建一个类,该类继承Thread类
(2)在该类中重写Thread的run()方法,方法中写想要单独执行的代码
(3)创建Thread实现类的对象
(4)实现类的对象调用对象的start()方法。start()继承自Thread类其中调用了run()方法
③注意:
(1)一个子线程只能被start()一次
(2)不能通过对象直接调用run()方法启动线程
④为什么调用start()方法启动线程(伪代码)
class Thread{ <span style="white-space:pre"> </span>public void run(){ <span style="white-space:pre"> </span>System.out.println("a"); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>public void start(){//子类重写Thread的run方法后会调用子类的run <span style="white-space:pre"> </span>run(); <span style="white-space:pre"> </span>} } class MyThread extends Thread{ <span style="white-space:pre"> </span>public void run(){ <span style="white-space:pre"> </span>System,out.println("b"); <span style="white-space:pre"> </span>} }
1/2 方式二:实现Runnable接口
(1)创建一个类,该类实现Runnable接口
(2)在该类中重写run()方法,该方法中实现子线程的功能
(3)创建该类的对象
(4)创建Thread类对象的引用,将所创建的类的对象作为形参传递给Thread类的构造器
(5)Thread类对象的引用调用start()方法启动线程
1/3 实现方式三:实现Callable接口(JDK 5)
(1)创建一个类,实现Callable接口
(2)重写call()方法
(3)获取一个线程池
(4)调用线程池的submit()方法启动线程,将实现类的对象作为参数传递进去
(5)关闭线程
1/4 继承thread类与实现Runnable接口的比较
(1)Thread类也实现了Runable接口,class Thread implements Runable{}
(2)实现Runable接口的方式优于继承Thread类的方式
原因:①避免了java单继承的局限性
②如果多个线程操作同一份资源(数据),实现Runnable接口的方式更合适
2、线程中的常见方法
2/1 getName()获取线程的名称
2/2 currentThread()获取当前线程
2/3 setName() 设置线程的名称
2/4 run()子线程要执行的代码放在run()方法中
2/5 start()启动线程
2/6 setPriority(() 设置线程的优先级
2/6 getPriority()获取线程的优先级
2/7 yield() 当前线程释放CPU资源,退回到就绪状态,同其他线程一起等待操作系统分配资源。释放后可能还会抢回资源。yield以后可能会马上
又抢回资源,而且不保证一定是此线程最后执行完成
2/8 join() 在A线程中调用B线程的join()方法,表示当执行到此方法时A线程停止执行,直到B线程执行完毕,A线程在继续执行。join()的进程与在其之前已经启动的线程会同时执行,其后的线程会在其执行完成后再执行。其后的线程与其之前的线程会同时执行
2/9 isAlive() 判断当前线程是否存活
2/10 sleep(long l) 显示的让线程沉睡一段时间
2/11 Thread.setDeamon(true):设置守护线程
2/12 wait():让当前访问的线程等待,只能在同步中使用
2/13 notify():唤醒线程,只能在同步中使用
2/14 notifyAll(): 唤醒线程,只能在同步中使用
六、线程的优先级:
1、java使用抢占式调度模型
2、优先级的范围是1---10
3、默认的优先级为5
4、可以通过setPriority()设置线程的优先级
可以通过getPriority()获取线程的优先级
5、线程的优先级依赖于操作系统的调度,需要跟操作系统配合,不能保证优先级高的线程一定先执行完毕,所以不能依赖于使用线程的优先级试图让某个线程先执行完毕
6、如果线程内部的业务逻辑过于简单,线程的优先级将看不出效果
七、多线程的优点
1、提高应用程序的响应,对图形化界面更有意义,可增加用户体验
2、提高计算机系统CPU的利用率
3、改善程序结构。将既长又复杂的进程分为多个线程,独立运行,利于理解和修改
八、线程的分类
1、非守护线程:
当主方法结束时,主线程开出的线程仍在继续运行,直到运行完成,这个就是非守护线程
2、守护线程
2/1 当主进程结束时,守护线程也跟着结束,但会有一点缓冲,不会立马结束
2/2 守护线程是用来服务用户线程的,通过在调用start()方法前调用Thread.setDeamon(true)方法可以将用户线程变成守护线程。
2/3 垃圾回收是一个典型的守护线程
2/4 若JVM中只有守护线程,则JVM退出
九、线程的中断
1、可以在主进程汇总结束某个线程
2、中断方式:
方式一:使用stop()方法
已过时,不安全
方式二:interrupt:当线程处于:Object--wait()及Thread--join()以及Thread---sleep()受阻时,此方法调用将会导致上述三个方法阻塞时的异常抛出
十、线程的生命周期
1、新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态
2、就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件
3、运行:当就绪的线程被调度并获得处理器资源时,便进入运行状态, run()方法定义了线程的操作和功能
4、阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态
5、死亡:线程完成了它的全部工作或线程被提前强制性地中止
十一、线程安全
1、线程安全存在的原因
由于一个线程在操作共享数据的过程中,为执行完毕的情况下,另一个线程参与进来,导致共享数据出现了安全问题
2、解决线程的安全问题
必须让一个线程操作共享数据完毕后,才允许其他线程参与进来
3、实现线程的安全,线程的同步机制
方式一、同步代码块
synchronized(同步监视器){
//需要被同步的代码块(即操作共享数据代码)
}
共享数据:多个线程共同操作的同一个数据(一个变量)
同步监视器:由任何一个类的对象充当。获取此监视器的线程执行大括号中被同步的代码。俗称:锁
要求所有的线程必须共用同一把锁
方式二、同步方法
public synchronized void show(String name){ <span style="white-space:pre"> </span>//代码 }
4、线程同步的弊端:由于同一个时间只能有一个线程访问共享数据,所以效率降低了
5、静态方法中的同步代码块不能锁this,一般锁这个类的class对象
publicstatic void show(){ <span style="white-space:pre"> </span>synchronized(Accound.class) { <span style="white-space:pre"> </span>System.out.println("show()"); <span style="white-space:pre"> </span>} }
静态方法可以是synchronized的
publicsynchronized static void show2(){ <span style="white-space:pre"> </span>System.out.println("show2()"); }
十二、锁的释放
1、释放锁的操作
当前线程的同步方法、同步代码块执行结束
当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行。
当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束
当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
2、不会释放锁的操作
线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行
线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。
应尽量避免使用suspend()和resume()来控制线程
十三、JDK5之后出现的Lock锁(Lock接口)
1、Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
2、使用方法:
Lock l = ...; //创建一个子类对象(直接子类;ReentrantLock,ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock) l.lock();//加锁 try { // 被同步的代码 } finally { l.unlock();//释放锁 }
十四、死锁
定义:
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
解决方法:
专门的算法、原则
尽量减少同步资源的定义
十五、线程通信
1、wait() 与 notify() 和 notifyAll()
wait():令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问
notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
notifyAll ():唤醒正在排队等待资源的所有线程结束等待.
Java.lang.Object提供的这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常
2、wait() 方法
2/1 在当前线程中调用方法: 对象名.wait()
2/2 使当前线程进入等待(某对象)状态 ,直到另一线程对该对象发出 notify (或notifyAll)为止。
2/3 调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)
2/4 调用此方法后,当前线程将释放对象监控权 ,然后进入等待
2/5 在当前线程被notify后,要重新获得监控权,然后从断点处继续代码的执行。
3、notify()/notifyAll()
在当前线程中调用方法: 对象名.notify()
功能:唤醒等待该对象监控权的一个线程。
调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)
十六、线程组(ThreadGroup)
1、默认线程组:
所有线程都默认属于一个线程组:main线程组
2、可以将一些线程放到一个指定的线程组内
2/1 构造方法
ThreadGroup(String name):创建一个指定名称的线程组
2/2 将线程放到线程组的步骤
1、创建一个线程组
2、实例化线程对象
3、实例化一个Thread,使用自定义线程组、线程、线程名称做参数
3、我们可以对一个线程组中的线程进行统一操作,比如停止线程组中的所有线程
十七、线程池(JDK 5之后)
1、对于同一个Thread对象,不能重复调用start()方法
2、若想再次使用这个Thread对象,只能重新创建一个对象再调用
3、这种情况下可以使用线程池
1/1 他可以缓存已经执行过的线程
1/2 对于存在与缓存池中的线程可以直接执行
1/3 Executors类
1/3/1 成员方法:
public static ExecutorService newCachedThreadPool():创建一个可根据需要创建新线程的线程池,但是在以前构
造的线程可用时将重用它们。
public static ExecutorServicenewFixedThreadPool(int nThreads):创建一个可重用固定线程数的线程池,
public static ExecutorServicenewSingleThreadExecutor():创建一个使用单个 worker 线程的Executor,以无界队列
方式来运行该线 程。新创建的单线程 Executor
返回值:ExecutorService(类):
Future<?> submit(Runnable task):执行并缓存线程对象;
停止线程:shutdown();
4、创建步骤
1、创建一个线程池
ExecutorServiceservice = Executors.newFixedThreadPool(2);
2、创建线程类对象
MyThreadt4 = new MyThread();
MyThreadt5 = new MyThread();
3、使用线程池的submit()方法启动线程,将线程对象作为参数传递给submit()方法
service.submit(t4);//执行t4并缓存t4
service.submit(t5);//执行并缓存t5
4、关闭线程池
十八、匿名内部类的方式实现多线程
1、new Thred(){匿名的Thread的子类}
2、new Thread(){匿名的Runnable的子类}
3、new Thread(匿名的Runnable的子类){匿名的Thread的子类},最终执行Thread子类的run()方法
public class Demo { <span style="white-space:pre"> </span>public static void main(String[] args) { <span style="white-space:pre"> </span>//1.new Thread(){匿名的Thread的子类}; <span style="white-space:pre"> </span>new Thread(){ <span style="white-space:pre"> </span>//重写run() <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void run() { <span style="white-space:pre"> </span>System.out.println("a"); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>}.start(); <span style="white-space:pre"> </span>//2.new Thread(匿名的Runnable的子类){}; <span style="white-space:pre"> </span>new Thread(new Runnable(){ <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void run() { <span style="white-space:pre"> </span>System.out.println("b"); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>}){}.start(); <span style="white-space:pre"> </span>//3.new Thread(匿名的Runnable的子类){匿名的Thread的子类}; <span style="white-space:pre"> </span>new Thread(new Runnable(){ <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void run() { <span style="white-space:pre"> </span>System.out.println("c"); <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>}){ <span style="white-space:pre"> </span>@Override <span style="white-space:pre"> </span>public void run() { <span style="white-space:pre"> </span>System.out.println("d");//最终执行的是子类的run() <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>}.start(); <span style="white-space:pre"> </span>} }
十九、定时器
1、在指定的时间,完成指定的任务
2、在指定的时间开始,每个多长时间,重复的执行指定的任务
3、定时器的实现
3/1 使用到的类:TimerTask、Timer
3/1/1 TimerTask:描述具体的任务
①定义类,继承自TimerTask;
②重写run()方法,将具体的任务写到这里
3/1/2 Timer:启动TimerTask任务
①构造一个Timer对象,使用无参构造方法
②调用方法
public void schedule(TimerTask task,longdelay):在指定的delay时间后,执行task任务,只执行一次
public void schedule(TimerTask task,longdelay,long period):在指定的delay时间后,执行task任务,每隔
period时间执行一次
二十、wait()/sleep()/yield()的区别
wait()是Object类的方法,会释放锁,可以指定时间,使用notif()或者notifyAll()唤醒
sleep()是Thread类的方法,不会释放锁,必须指定时间,时间到后会自动唤醒
yield()是Thread类的方法,不会释放锁,使当前访问的线程退回到就绪状态,但是,当在"同步"方法中使用时,不会释放锁;
二十一、面试题
1.多线程有几种实现方案,分别是哪几种?三种
1).继承Thread类,重写run()方法
<span style="white-space:pre"> </span>class MyThread extends Thread{ public void run(){ } } main(){ MyThread t = new MyTrhead(); t.start(); }
2).实现Runnable接口,重写run()方法
<span style="white-space:pre"> </span>class MyRunnable implements Runnable{ public void run(){ } } main(){ MyRunnable myRun = new MyRunnable(); Thread t = new Thread(myRun); t.start(); }
3).实现Callable接口,重写:call()方法
<span style="white-space:pre"> </span>class MyCallable implements Callable{ public Object call() throws Exception{ return null; } } main(){ //1.获取一个线程池 ExecutorService service = Executors.newFixedThreadPool(2); //2.执行submit()方法: Future result = service.submit(new MyCallable()); //获取返回值 Object value = result.get(); service.shutdown(); }
2.同步有几种方式,分别是什么?
1).同步代码块:
synchronized(被锁的对象){
}
被锁的对象:如果有一个线程访问此对象的某个同步代码块时,意味着这个线程当前锁住了整个对象,其它线程无法访问 此对象内的所有同步的代码块及同步方法。
A.在某个类中的同步代码块,通常锁的对象设定为:this;
2).同步方法:
public synchronized void show(){//锁的对象,默认为当前对象;
}
静态的方法内可以包含"同步代码块",一般锁的对象是当前类的class对象。
静态方法也可以是"同步的"。
3.启动一个线程是run()还是start()?它们的区别?
是start()方法。
区别:
1.run():重写父类的方法,需要线程执行的内容,写到这里;
2.start():启动线程,内部会自动调用run();
4.sleep()和wait()方法的区别:
1.sleep():Thread类的方法;在同步方法内,不释放锁;必须指定时间,当时间到时,会自动醒来;
2.wait():Object类的方法;在同步方法内,会释放锁;可以指定时间,也可以不指定时间。使用notify()或notifyAll()唤醒,或者时间到了,自动醒来;
5.为什么wait(),notify(),notifyAll()等方法都定义在Object类中:
任何对象都可能会被多个线程并发访问,所以要求任何对象都有wait()和notify()的功能,所以就定义在了Object类中。
6.线程的生命周期图:
基本的声明周期:新建-->就绪-->运行-->死亡
标签:
原文地址:http://blog.csdn.net/hijson/article/details/51216451