Java中的程序是多线程的, 一个简单的程序中包含了两个线程, 即main程序主入口,还有个是gc ,垃圾回收机制的线程。
线程跟进程的区别:
进程是程序中执行的一个过程,它是一个动态的概念, 进程最少有5种基本状态,包含了,初始-->执行--->等待-->就绪--> 终止。
线程是进程的一部分,一个没有线程的进程以可看成是一个单线程,线程有时又被称之为轻量级进程或是轻权进程,也是cpu调度的一个基本单位。
进程的执行过程是线状的,线程只改变cpu的执行过程的改变,而没有发生进程所拥有的资源变化,进程拥有一个完整的虚拟空间,不依赖于线程而独立存在。 反之,线程只是进程的一部分,没有自己的地址空间,与进程内的其他线程共享分配该进程内的所有资源。
线程的执行特性: 就绪--->执行--->阻塞
Java中的线程
实现: 可以继承自Thread类 , 或是实现runnable 接口 (推荐使用这种)
Thread t=new Thread(); t.start(); -->启动线程
join 方法 线程A,线程B, 执行, 想让线程B执行完后, 在让执行A, 则可以使用join()
示例中的代码运行完thread.join()后,就是将主线程最后一个输出语句放到thread执行完后才执行,因为thread正常情况下永远执行不完,所以主线程永远不会执行。有一点需要注意的就是threa.join()必须放在thrad.start()后面才会起作用,放在前面是不起作用的。
yield:
操作系统执行的时候是按照cpu的算法执行各自的线程, 当线程调用yield的时候 可以让cpu不执行该当前线程,也就是将线程的执行状态切换成就绪状态,但是下一个线程要执行哪一个是要根据线程调度算法来确定的,我们并不能确定具体执行那个线程,也就是说有可能下一秒当前线程又切换成执行状态。
thread在index 为10的时候让出了cpu的控制权。
interrupt:
Thread.interrupt 是一个非静态的方法,它的作用是 “停止” 当前线程的执行,为啥要打上双引号呢,因为只是在特殊的情况下有用,像线程调用了join,wait,sleep 等可中断的阻塞方法后,调用interrupt 就会抛出interruptedException 异常。其实,它称为协作停止。调用这个方法,特殊情况外的其他情况下,并不会停止。它发送一个停止请求,并且由线程记录下来(实际上就是有一个为bool的变量,当为true时就表示有停止线程运行请求)。所以说线程真正的停止需要我们自己去检查是否有停止线程的请求,当有线程停止请求时停用当前线程。和它配套使用的有 :
1.public static boolean interrupted()
这个方法是检查当前线程是否有停止请求,需要注意的是这个方法调用过后会重置请求标志位。如果我们第一次调用这个方法得到结果是true,那么这个时候它会重置标志位,第二次调用这个方法得到的结果是false。
2.public boolean isInterrupted()
这个方法是判断调用该方法线程是否需要停止,它和静态的interrupted()有一点很不同的是它不会去重置标志位。也就是说我们用这个方法查询第一次是true的话,以后那次查询都还是true,并不会改变。
例:
像上面这个例子,thread调用interrupt停用当前线程,但是线程并不会停止。而是继续输出
如果我们这样写
就可以实现了,但是这样非常耗时,每次都会去检查是否需要停止。检查的具体算法就要看我们具体的需求了。
sleep , wait 方法对比
sleep 是thread类的静态方法,不能改变对象的机锁,让线程进入休眠状态, 当前线程调用该方法,让出cpu时间片, 不会释放对象锁, 其他线程依然无法访问该对象。
wait 是Object类的方法, 必须配套这同步方法使用。让线程进入等待状态,让出cpu时间片,并释放对象监视器的所有权(对象锁),使得其他线程能够访问,等待其他线程通过notify方法来唤醒。
同步方法的三种实现:
1.使用同步代码块 (this, 定义同一个对象就行。Object o=new Object();)
2.同步方法 (定义个方法,使用synchroinzed 修饰就行)
3.互斥锁(使用Lock 代码更灵活)
互相排斥的锁, ReetrentLock lock=new ReetrentLock(); Lock 实现同步
private void method(){
Lock.lock(); 锁
if(ticket>0{
ticket--; //模拟火车票售卖,火车票的总票数固定,在不同窗口售卖, 需要保证一张票被一个人购买到,同时总票数要逐渐递减,保证票数在各大售票窗口一致。使用同步锁的方法·。保证订票时,在各大售票窗口请求同一个对象,看谁的手势快,抢占该个对象执行,则将该车票售卖给谁,第二位则不能改对象, 进入下一轮的购票。
try{
Thread.sleep(millis:5000); //5s
}catch(InterruptedException e){
e.printTraceaction();
}finally{
lock.unlock(); //释放锁
}
}
}
死锁产生的情况
一、死锁原理
a、根据操作系统中的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。
二、死锁的四个必要条件:
a、互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程使用。
b、请求与保持条件(Hold and wait):已经得到资源的进程可以再次申请新的资源。
c、非剥夺条件(No pre-emption):已经分配的资源不能从相应的进程中被强制地剥夺。
d、循环等待条件(Circular wait):系统中若干进程组成环路,该环路中每个进程都在等待相邻进程正占用的资源。
三、避免死锁
a、按同一顺序访问对象。
b、避免事务中的用户交互。
c、保持事务简短并处于一个批处理中。
d、使用较低的隔离级别。
多线程产生的死锁:
1.过多的同步可能会出现死锁, 死锁的操作一般是在程序运行时候才有可能出现
2.在同一个同步方法中调用不同的对象方法。