一、java线程基础知识
进程是操作系统分配资源的基本单位
线程是cpu调度的基本单位
jdk线程是协作式的不是抢占式的,操作系统线程是抢占式的
死锁不理会中断
synchronized等待不可以被中断,代码可以被中断
1、停止线程的方法:
1、这四个方法已被废弃
stop()(强行停止线程)
destroy()
resume()
suspend()(挂起一个线程,但是不释放琐资源)
2、interrup()将中断标志位置为true
isInterrupted() 判断中断标志是否为true
static方法interrupted()判断中断标志是否为true;如果中断标志为true,并且将中断标志位置为false
sleep,wait,join方法被中断后,后会出现抛出InterruptException异常的情况
reentrantLock.lockInterruptibly();
当线程抛出InterruptException异常以后,中断标志位会被复位。
特别注意的是需要在InterruptException的catch语句里添加Thread.currentThread().interrupt();进行再次中断,否则会出现死循环的现象。
public class Main { public static void main(String[] args) throws Exception { Thread t = new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub while (!Thread.currentThread().isInterrupted()) { try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block System.out.println("1、" + Thread.currentThread().isInterrupted()); Thread.currentThread().interrupt(); e.printStackTrace(); } System.out.println("2、" + Thread.currentThread().isInterrupted()); } System.out.println("3、" + Thread.currentThread().isInterrupted()); } }); t.start(); Thread.sleep(500); t.interrupt(); } }
2、创建线程的方法
Thread,Runnable,Callable
thread的start方法只可以执行一次;run方法可以执行多次。
利用Callable创建线程的方法:
import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; public class Main { public static void main(String[] args) throws Exception { FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() { @Override public String call() throws Exception { // TODO Auto-generated method stub System.out.println("callable!"); return "callableResult"; } }); new Thread(futureTask).start(); System.out.println(futureTask.get()); } }
3、守护线程与非守护线程
main主线程结束,子线程仍会继续运行。
只有当所有的非守护线程都运行结束,jvm才会退出。
守护线程的finally语句是不能保证执行的。用户线程finally一定会执行。
4、线程的其他方法
yield()线程主动让出cpu执行权;将线程从运行状态到就绪状态
线程优先级为1~10,默认值为5
yield()与sleep():执行yield方法后线程到就绪状态,执行sleep方法后线程没有到就绪状态
join()方法
5、线程间的协同机制
(1)synchronized内置锁(对象锁和类锁)【本质就是对象锁,对象头里的标志】
对象锁和类锁之间是相互独立的,相互之间是不干扰的
类锁锁的是Class对象
类锁和静态变量的锁是可以并行的,因为他们锁的不是同一个对象
synchronized的本质就是锁对象
System.identityHashCode(Object x)返回对象原生的HashCode
(2)volatile
只有一个线程写, 其它线程读的场景。
(3)ThreadLocal
多线程环境下可以随意使用
定义成private static
为每一个线程都提供了变量的副本
线程池的时候使用ThreadLocal
spring的事务处理有用到(保证是在同一个连接里)把这个连接绑定到threadlocal里面
ThreadlLocal弱引用
ThreadLocal就是一个变量的包装类,可以这样子理解
ThreadLocal最好用private static修饰
Thread---->ThreadLocalMap---->Entry(Threadlocal1,value1)
---->Entry(Threadlocal2,value2)
---->Entry(Threadlocal3,value3)
ThreadLocalMap的底层是一个Entry数组,使用hash表插入,用开放寻址法解决冲突问题
(4)wait()/notify()/notifyAll()/wait(1000ms)
这些方法都是Object的方法
(5)join()方法【可以实现线程串联排队执行】保证两个线程顺序的执行
怎么保证一个线程在另一个线程之前执行完?join(),countDownLatch()
(6)锁的机制:yield()方法,交出cpu执行权,但是不释放锁
sleep()方法,交出cpu执行权,但是不释放锁
wait()方法之前必须持有锁,方法之后必须释放锁,当wait()方法返回的时候,线程必须重新持有锁
notify()方法之前必须持有锁,方法之后同步代码块执行完才会释放锁
wait/notify标准范式
//通知 synchronized (对象) { while(条件不满足) { 对象.wait(); } //业务逻辑 } //等待 synchronized (对象) { //业务逻辑,改变条件 对象.notify()/notifyAll() }
1、中断机制
如果线程堵塞在object.wait、Thread.join和Thread.sleep,将会清除线程的中断状态,并抛出InterruptedException;
如果线程堵塞在java.nio.channels.InterruptibleChannel的IO上,Channel将会被关闭,线程被置为中断状态,并抛出java.nio.channels.ClosedByInterruptException;
如果线程堵塞在java.nio.channels.Selector上,线程被置为中断状态,select方法会马上返回,类似调用wakeup的效果;
如果不是以上三种情况,thread.interrupt()方法仅仅是设置线程的中断状态为true。
2、中断标志位的设置
Thread.interrupt();只是改变线程的中断状态。
object.wait、Thread.join和Thread.sleep这三个状态下的线程才会不停的检测中断标志,其他状态下的线程是不会检测中断标志的。