Java 线程
1. 什么是线程:线程就是程序中单独顺序的流控制。线程本身不能运行,它只能用于程序中。
2. 什么是多线程:多线程则指的是在单个程序中可以同时运行多个不同的线程执行不同的任务。
3. 线程是程序内的顺序控制流,只能使用分配给程序的资源和环境。
4. 多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU,而只和I/O等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。
5. 进程:执行中的程序(程序是静态的概念,进程是动态的概念)。一个进程可以包含一个或多个线程。
6. 线程与进程的区别:
a. 资源是否独立:多个进程的内部数据和状态都是完全独立的,而多线程是共享一块内存空间和一组系统资源,有可能互相影响。线程本身的数据通常只有寄存器数据,以及一个程序执行时使用 的堆栈,所以线程的
切换比进程切换的负担要小。
b. 切换成本:多线程程序比多进程程序需要更少的管理费用。进程是重量级的任务,需要分配给它们独立的地址空间。进程间通信是昂贵和受限的。 进程间的转换也是很需要花费的。另一方面,线程是
轻量级的选手。它们共享相同的地址空间并且共同分享同一个进程。线程间通信是便宜的,线程间的转换也是低成本的。
8. 线程的实现有两种方式,第一种方式是继承 Thread 类,然后重写 run 方法;第二种是实现 Runnable 接口,然后实现其 run 方法。
9. 将我们希望线程执行的代码放到 run 方法中,然后通过 start 方法来启动线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法。当某个类继承了Thread 类之后,该类就叫做一个线程类。
10. Thread 类也实现了 Runnable 接口,因此实现了 Runnable 接口中的 run 方法;
11. 当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该该number将是自动增加的,并被所有的Thread对象所共享(因为它是 static 的成员变量)。推荐
自己命名,方便后续调试等。
12. 关于成员变量与局部变量:如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成员变量的改变会影响到另一个线程)。如果
一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响到其他的线程。
13. 停止线程的方式:不能使用 Thread 类的 stop 方法来终止线程的执行。 一般要设定一个变量,在 run 方法中是一个循环,循环每次检查该变量,如果满足条件则继续执行,否则跳出循环,线程结束。
14. 不能依靠线程的优先级来决定线程的执行顺序。
15. synchronized 关键字:当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
16. Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的 synchronized 方法时,表示将该对象上锁,此时他任何线程都无法再去访问该synchronized方法了,
直到之前的那个线程执行方法完毕后(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该 synchronized 方法。
17. 如果一个对象有多个synchronized方法,某一时刻某个线程已经进入到了某个synchronized方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何synchronized方法的。
18. 如果某个 synchronized 方法是 static 的,那么当线程访问该方法时,它锁的并不是synchronized 方法所在的对象, 而是 synchronized方法所在的对象所对应的Class对象,因为Java中
无论一个类有多少个对象,这些对象会对应唯一一个 Class 对象,因此当线程分别访问同一个类的两个对象的两个 static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个
线程先去执行方法,执行完毕后另一个线程才开始执行。
19. synchronized 块,写法:
synchronized(object){
}
表示线程在执行的时候会对 object 对象上锁。
20. synchronized 方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized 方法;synchronized 块则是一种细粒度的并发控制, 只会将块中的代码同步,位于方法内、
synchronized 块之外的代码是可以被多个线程同时访问到的。
21. wait与notify方法都是定义在Object类中,而且是final的,因此会被所有的Java类所继承并且无法重写。这两个方法要求在调用时线程已经获得了对象的锁,因此对这两个方法的调用需要放在
synchronized方法或块当中。当线程执行了wait方法时,它会释放掉对象的锁。
22. 另一个会导致线程暂停的方法就是Thread类的sleep方法,它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。
23. 线程的生命周期可分为四个状态:1. 创建状态 2. 可运行状态 3. 不可运行状态 4. 消亡状态
线程从创建到最终的消亡,要经历若干个状态。一般来说,线程包括以下这几个状态:创建(new)、就绪(runnable)、运行(running)、阻塞(blocked)、time waiting、waiting、消亡(dead)。
24. 线程调度器选择优先级最高的线程运行。但是,如果发生以下情况,就会终止线程的运行:
1). 线程体中调用了yield()方法,让出了对CPU的占用权
2). 线程体中调用了sleep()方法 , 使线程进入睡眠状态
3). 线程由于I/O操作而受阻塞
4). 另一个更高优先级的线程出现。
5).在支持时间片的系统中,该线程的时间片用完。
25. isAlive() 判断线程是否已消亡
26. 当synchronized方法执行完或发生异常时,会自动释放锁。
27. 所有线程都隶属于一个线程组。那可以是一个默认线程组,亦可是一个创建线程时明确指定的组。说明:在创建之初,线程被限制到一个组里,而且不能改变到一个不同的组。
若创建多个线程而不指定一个组,它们就会与创建它的线程属于同一个组
28. 定时任务:
1). Timer和TimerTask一起工作。Timer是一个用于安排一个将来执行的任务的类。被安排的任务必须是TimerTask的一个实例。因此,为了
安排一个任务,首先应该创建一个TimerTask对象,然后使用Timer的一个实例安排执行它。
2). TimerTask实现了Runnable接口;因此它可以被用于创建一个执行线程。它的构造函数如下所示:TimerTask( )
3). TimerTask的run( )是一个抽象方法,这意味着它可以被覆盖。由Runnable接口定义的run( )方法包含了将被执行的程序代码。因此创建
一个定时器任务的最简单的办法是扩展TimerTask和重写run( )
4). 一旦任务被创建,它将通过一个类型Timer的对象被安排执行。Timer的构造函数如下:
Timer( )
Timer(boolean DThread)
5). 第一种形式创建一个以常规线程方式运行的Timer对象。第二种形式当DThread为true时,使用后台线程。只要剩下的程序继续运行,
后台线程就会执行。
6). 一旦Timer被创建,将可以通过调用创建的Timer的schedule( )方法来安排任务。有几种schedule( )方法的形式,这些形式允许用各种
办法来安排任务。
29. yield方法
调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。
30. join方法
join方法有三个重载版本:
1
2
3
|
join() join( long millis) //参数为毫秒 join( long millis, int nanoseconds) //第一参数为毫秒,第二个参数为纳秒 |
假如在main线程中,调用thread.join方法,则main方法会等待thread线程执行完毕或者等待一定的时间。如果调用的是无参join方法,则等待thread执行完毕,如果调用的是指定了时间参数的join方法,则等待一定的时间。
31. 以下是关系到线程属性的几个方法:
1)getId:用来得到线程ID,以此来判断是否为同一个线程
2)getName和setName:用来得到或者设置线程名称。
3)getPriority和setPriority:用来获取和设置线程优先级。
4)setDaemon和isDaemon:用来设置线程是否成为守护线程和判断线程是否是守护线程。
32. synchronized方法有几点需要注意:
1). 当一个线程正在访问一个对象的synchronized方法,那么其他线程不能访问该对象的其他synchronized方法。这个原因很简单,因为一个对象只有一把锁,当一个线程获取了该对象的锁之后,其他线程无法获取该对象
的锁,所以无法访问该对象的其他synchronized方法。
2). 当一个线程正在访问一个对象的synchronized方法,那么其他线程能访问该对象的非synchronized方法。这个原因很简单,访问非synchronized方法不需要获得该对象的锁,假如一个方法没用synchronized关键字修
饰,说明它不会使用到临界资源,那么其他线程是可以访问这个方法的,
3). 如果一个线程A需要访问对象object1的synchronized方法fun1,另外一个线程B需要访问对象object2的synchronized方法fun1,即使object1和object2是同一类型),也不会产生线程安全问题,因为他们访问的是
不同的对象,所以不存在互斥问题。
33. synchronized与ReentrantLock比较:
1). 并发包下面的锁分为:Lock(实现类ReentrantLock)和ReadWriteLock(读写锁,实现类ReentrantReadWriteLock。它的readLock()方法获得的锁,实现了Lock接口)
2). ReentrantLock更加灵活(包括获取锁:尝试获取锁,设置获取锁的时间;阻塞唤醒等)
3). Lock需要手动释放锁,synchronized会自动释放锁。包括异常情况,所以Lock要把锁的释放写到finally代码块中。
4). 并发量小的时候synchronized效率更高(貌似JVM有优化),并发量大的时候Lock效率更高。
读写锁的使用方法:
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
rwl.readLock().lock();
rwl.readLock().unlock();
总的来说,lock更加灵活。
主要相同点:Lock能完成synchronized所实现的所有功能
不同:
1. ReentrantLock功能性方面更全面,比如时间锁等候,可中断锁等候,锁投票等,因此更有扩展性。在多个条件变量和高度竞争锁的地方,用ReentrantLock更合适,ReentrantLock还提供了Condition,
对线程的等待和唤醒等操作更加灵活,一个ReentrantLock可以有多个Condition实例,所以更有扩展性。
2. ReentrantLock必须在finally中释放锁,否则后果很严重,编码角度来说使用synchronized更加简单,不容易遗漏或者出错。
3. ReentrantLock 的性能比synchronized会好点。
4. ReentrantLock提供了可轮询的锁请求,他可以尝试的去取得锁,如果取得成功则继续处理,取得不成功,可以等下次运行的时候处理,所以不容易产生死锁,而synchronized则一旦进入锁请求要么成功,
要么一直阻塞,所以更容易产生死锁。
转载:
1. ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
ReentrantLock获取锁定与三种方式:
a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
c) tryLock (long timeout, TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断
2. synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,
要保证锁定一定会被释放,就必须将 unLock()放到inally{} 中
3. 在资源竞争不是很激烈的情况下,Synchronized(JVM有优化)的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;
参见:
posted on 2016-07-26 19:26 alvin.zhang 阅读(124) 评论(0) 编辑 收藏 举报