主要框架内容:
线程概念,线程的生命周期,线程的实现,线程的优先级,多线程的同步,线程组,线程的状态图
1.什么是线程:
线程就是程序中单独顺序的流控制。线程本身不能运行,它只能用于程序中。
2. 什么是多线程:
多线程则指的是在单个程序中可以同时运行多个不同的线程执行不同的任务.
说明:
线程是程序内的顺序控制流,只能使用分配给程序的资源和环境,线程一旦运行,就不能控制了.没法控制谁先执行,怎么交叉执行
多线程编程的目的,就是"最大限度地利用CPU资源",当程序启动运行时,就自动产生一个线程,主方法main就在这个主线程上运行
线程的实现:
在Java中通过run方法为线程指明要完成的任务,有两种技术来为线程提供run方法。
1.继承Thread类并重写run方法。
2.通过定义实现Runnable接口的类进而实现run方法。
要想让对象作为一个线程运行,你必须调用start方法.否则就是一个普通的对象了.
通过对Thread源码的分析:第一种方式和第二种方式的区别,第一种方式时,那么target就为空了,如果不重写run方法的话,那么它是什么也不做的,如果第二种的话,则target!=null,那么就调用我们传过去的那个实现了Runnable接口的target的run方法.
start0()这个方法是个native方法,就是这个方法会去调用run方法的.
1) Thread类也实现了Runnable接口,因此实现了Runnable接口中的run方法;
2) 当生成一个线程对象时,如果没有为其设定名字,那么线程对象的名字将使用如下形式:Thread-number,该number将是自动增加的,并被所有的Thread对象所共享(因为它是static的成员变量)。
3) 当使用第一种方式来生成线程对象时,我们需要重写run方法,因为Thread类的run方法此时什么事情也不做。
4) 当使用第二种方式来生成线程对象时,我们需要实现Runnable接口的run方法,然后使用new Thread(new MyThread())(假如MyThread已经实现了Runnable接口)来生成线程对象,这时的线程对象的run方法就会调用MyThread类的run方法,这样我们自己编写的run方法就执行了。
关于成员变量与局部变量
如果一个变量是成员变量,那么多个线程对同一个对象的成员变量进行操作时,他们对该成员变量是彼此影响的(也就是说一个线程对成员变量的改变会影响到另一个线程)。
如果一个变量是局部变量,那么每个线程都会有一个该局部变量的拷贝,一个线程对该局部变量的改变不会影响到其他的线程。
停止线程的方式:不能使用Thread类的stop方法来终止线程的执行。一般要设定一个变量,在run方法中是一个循环,循环每次检查该变量,如果满足条件则继续执行,否则跳出循环,线程结束。
不能依靠线程的优先级来决定线程的执行顺序。因为有可能现在的优先级低的过了一段时间之后它的优先级提高了,这跟算法是有关系的.
关于线程的同步:(synchronized)
synchronized关键字用在两个地方:一是用synchronized修饰方法,一个是synchronized块
Java中的每个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法了,直到之前的那个线程执行方法完毕后(或者是抛出了异常),那么将该对象的锁释放掉,其他线程才有可能再去访问该synchronized方法。
如果某个synchronized方法是static的,那么当线程访问该方法时,它锁的并不是synchronized方法所在的对象,而是synchronized方法所在的对象所对应的Class对象,因为Java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的两个static,synchronized方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。
所以静态的就是锁Class对象,非静态的就是锁的是当前类的实例对象.
synchronized(obj)同步代码块,实际上里面的obj你声明成任何的对象都是可以的,它并没有实际的作用,只是用来声明锁了一个对象而已.
synchronized块,写法:
synchronized(object)
{
}表示线程在执行的时候会对object对象上锁。
synchronized方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized方法;synchronized块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、synchronized块之外的代码是可以被多个线程同时访问到的。
wait与notify方法都是定义在Object类中,而且是final的,因此会被所有的Java类所继承并且无法重写。这两个方法要求在调用时线程应该已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或块当中。当线程执行了wait方法时,它会释放掉对象的锁。这两个方法一定是成对出现的.主要是用作为线程与线程之间的通信.
完整的线程状态图:
说明:
当前一个线程把锁释放掉之后你就成为Runnable状态,此时也不是说你就一定能Running,因为有可能你还没有获得CPU资源啊.
注意里面两个池:
The pools:
– Wait pool(等待池)
– Lock pool(锁池)
1.创建状态
•当用new操作符创建一个新的线程对象时,该线程处于创建状态。
•处于创建状态的线程只是一个空的线程对象,系统不为它分配资源
2. 可运行状态
•执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体—run()方法,这样就使得该线程处于可运行( Runnable )状态。
•这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行。
3.不可运行状态(Blocked)
当发生下列事件时,处于运行状态的线程会转入到不可运行状态。
•调用了sleep()方法;
•线程调用wait方法等待特定条件的满足
•线程输入/输出阻塞
返回可运行状态:
•处于睡眠状态的线程在指定的时间过去后
•如果线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件的改变
•如果线程是因为输入/输出阻塞,等待输入/输出完成
4. 消亡状态
当线程的run方法执行结束后,该线程自然消亡
notify方法唤醒其他的线程,当然是要在同一个对象的线程,以获得锁,唤醒之后并不一定就是能立即获得锁,有很多等待的线程在那里,只有获得锁的线程才能再次运行
多线程的一般写法:
while(...){
//处理 if(...)break;
...
...
}