程序:是一段静态的代码
进程:是程序的一次动态执行,对应代码的加载、运行和结束。进程有自己的专用内存区域
线程:比进程的单位小,没有自己的内存区域,多个线程之间可以共享对应进程的内存区域(包括代码和数据),并利用这些共享单元来实现数据交换、实时通信与必要的同步操作。
每个java程序都有一个主线程。jvm发现main方法就开始执行,当主线程及所有子线程都结束时,程序结束。每个线程对应一个任务,多任务对应为多线程。
调度方式:抢占式调度和协作式调度。
抢占式调度:直接中断而不需要和被中断程序协商
协作式调度:中断时需要协商
目前大部分操作系统都采用抢占式调度,小型设备,如手机,则采用协作式调度。
线程的状态及对应的java代码:
1、新建:
当一个thread类或其子类对象呗声明并创建,那么线程对象就处于新建状态,此时该线程已经拥有了相应的内存空间及其他的资源
2、运行:
当调用start方法时,线程开始运行,并执行run中的方法。在run中,不应该再调用start方法,否则会发生异常
3、阻塞:
阻塞的原因有:
1、线程试图获得一个锁,但该锁正在被别的线程占用。
2、当前线程执行了sleep方法或者wait方法,则线程挂起。若执行wait方法,则需要等待其他线程执行notify方法或者notifyall方法来唤醒他,之后该线程进入队列等待CPU资源。若执行sleep方法,则当sleep时间过了,该线程就可以进入队列等待CPU资源。
3、线程执行过程中,某个方法发生了阻塞,则线程挂起。当阻塞原因消除,那么线程进入线程队列等待CPU资源
4、线程等待一个触发条件,而该条件还没发生。
4、死亡:
线程执行完run方法
因为一个未捕获的异常而导致线程终止
注:当线程从阻塞状态恢复过来时,判断该线程与当前正在运行的线程的优先级,若该线程优先级比较高,则直接抢占CPU资源
线程的状态转换图如下所示:
优先级及调度顺序:
jvm线程调度器负责管理线程,线程的优先级分为1~10,主要使用的是1,5,10,尤其有些操作系统只识别这三种优先级。高的优先级的线程先运行,同等优先级的线程以轮流方式使用时间片。只有当高级别的线程死亡时,或者使用sleep方法或者wait方法挂起时,低级别的线程才能获得CPU资源。
守护线程:
这样的线程往往是为别的线程提供服务,在线程运行前设置。当程序只剩下守护线程时,程序退出
java中实现线程的方法:
1、继承Thread类,并重写run方法
2、实现runnable接口。
第二个方法更好,因为java不支持多继承,Thread子类不能再扩展其他的类。另外,使用runnable可以实现多线程间的资源共享。
以卖票为例:
若用Thread子类方法:
public MyThread extends Thread{
private int ticket = 10;
public void run(){
for(int i=0;i<20;i++){
if(this.ticket>0){
System.out.println("卖票:ticket"+this.ticket--);
}
}
};
public class ThreadTicket{
public static void main(String[] args)
{
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
mt1.start();
mt2.start();
mt3.start();
//运行结果,三个线程,每个线程卖了10张票,一共卖了30张票
}
使用runnable方法:
package
org.demo.runnable;
class
MyThread
implements
Runnable{
private
int
ticket=
10
;
public
void
run(){
for
(
int
i=
0
;i<
20
;i++){
if
(
this
.ticket>
0
){
System.out.println(
"卖票:ticket"
+
this
.ticket--);
}
}
}
} ;
package
org.demo.runnable;
public
class
RunnableTicket {
public
static
void
main(String[] args) {
MyThread mt=
new
MyThread();
new
Thread(mt).start();
//同一个mt,然而在Thread中就不可以,假如用同一
new
Thread(mt).start();
//个实例化对象mt,就会浮现 异样
new
Thread(mt).start();
//三个线程一共卖了10张票,因为都用了同一个runnable对象
}
};
//这里线程中run方法的步骤较少,因此可能不会出现同步错误。但是应该用synchronized关键字,或者ReentrantLock方法来保证同步。
线程的同步:
重点保证在每个时刻只能有一个线程能够进入临界区域。尽量用synchronized方法,这样可以减少代码量,也可以减少因为忘记写signal方法二引发错误。
JDK5.0之前通过synchronized方法或者synchronized块来实现,使用了这个关键字的方法或块每次只能被一个线程使用。
JDK5.0之后通过ReentrantLock来实现:
myLock.lock();
try{
....
}
finally{
mylock.unlock(); }
对域的安全访问:加violate关键字:
条件对象,或称条件变量:
在银行算法中,如果某个需要转出的账户中余额不足,则需要等待某个账户向该账户中存储钱才能继续转账操作。
Bank类构造函数:
转账函数:
//当余额不足时,用await方法阻塞。同时如果其他线程完成了转账过程,则唤醒因为余额不足而阻塞的线程。在当前线程完成了同步操作后,被唤醒的线程将进入竞争状态。
同样的功能,使用synchronized方法的代码如下:
死锁:
当所有的线程都阻塞了。