java多线程开发知识点汇总
1.引言
最近的工作大量运用了多线程的一些知识,才发现自己对这块知识有很多盲点,于是看看书,博客,写写,总结总结。
2.正题
1.线程的状态
- 新建
刚创建的时候的状态 - 就绪
等待获取cpu时间片 - 阻塞
阻塞状态的线程等待一个监视器锁以进入一个同步代码块/同步方法,或者该线程在调用wait()方法后重如该同步代码块/同步方法 - 运行
- 死亡
1.java Thread.yield 方法
yield
方法 让出当前Thread的cpu,使当前线程变成就绪态,和其他线程一起竞争cpu资源。yield方法并没有终止当前线程的意思。
2.线程池shutdown 和shutdownNow
/**
* Initiates an orderly shutdown in which previously submitted
* tasks are executed, but no new tasks will be accepted.
* Invocation has no additional effect if already shut down.
*
* <p>This method does not wait for previously submitted tasks to
* complete execution. Use {@link #awaitTermination awaitTermination}
* to do that.
*
* @throws SecurityException {@inheritDoc}
*/
shutdown
方法本质调用线程池各个线程的interrupt()方法,调用这个方法之后,线程池将不再接受新的任务,调用这个方法之后,再submit一个任务就会报RejectedExecutionException异常
/**
* Attempts to stop all actively executing tasks, halts the
* processing of waiting tasks, and returns a list of the tasks
* that were awaiting execution. These tasks are drained (removed)
* from the task queue upon return from this method.
*
* <p>This method does not wait for actively executing tasks to
* terminate. Use {@link #awaitTermination awaitTermination} to
* do that.
*
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. This implementation
* cancels tasks via {@link Thread#interrupt}, so any task that
* fails to respond to interrupts may never terminate.
*
* @throws SecurityException {@inheritDoc}
*/
shutdownNow
和shutdown方法一样,唯一不同,shutdownNow会尽最大的可能去终止正在运行的任务,并且返回没有执行的Runnable
3.Callable vs Runnable 接口
Runnable 执行完毕没有返回值
Callable执行完毕有返回值,通过Feture.get()获取返回值,此方法会使线程进入阻塞态
public static void main(String[] args) {
ThreadPoolExecutor m = (ThreadPoolExecutor) Executors.newCachedThreadPool();
Future <String>f=m.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(8000);
return "wxy";
}
});
System.out.println("开始阻塞");
try {
String s=f.get();
System.out.println(s);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("结束");
}
4.线程优先级
线程的等级在1-10之间,小于1 或者大于10 将报异常。优先级越高,获取cpu时间片越多,获取的几率也越大。通过setPriority方法设置
5.后台线程
后台线程,它是在后台运行的,它的任务是为其他线程提供服务,这种线程被称为“后台线程(Daemon Thread)”,又称为“守护线程”或“精灵线程”。JVM的垃圾回收线程就是典型的后台线程。当所有的用户线程(非守护线程)
当所有的前台线程死亡了,后台线程也会自动消亡,注意的是前台线程死亡,不等于睡眠
6.thread.join()
在线程a中调用线程b的join()方法,会中断线程a的执行,等待线程b执行完毕才会在继续执行线程a。
public static void main(String[] args) {
Thread thread2=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 10; i <20 ; i++) {
System.out.println(i);
}
}
});
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <10 ; i++) {
System.out.println(i);
if (i==5){
try {
thread2.start();
thread2.join();//放在start后面
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
thread1.start();
}
输出:
1 2 3 4 5 10 11 12 13 14 15 16 17 18 19 6 7 8 9
7.java Lock对象
synchronized 修饰的代码块或者方法,处于阻塞状态的时候,那么其他线程都无法访问这个方法,会一直阻塞下去。为了解决这种现象于是出现了Lock。Lock是一个接口,实现类是ReentrantLock ,ReentrantLock是一个可重入且独占式的锁,它具有与使用synchronized监视器锁相同的基本行为和语义,但与synchronized关键字相比,它更灵活、更强大,增加了轮询、超时、中断等高级功能。ReentrantLock,顾名思义,它是支持可重入锁的锁,是一种递归无阻塞的同步机制。除此之外,该锁还支持获取锁时的公平和非公平选择
8.volatile 关键字
volatile 关键字只能保证变量的可见性,在线程a中,被volatile修饰的变量a值改变,a将会通知其他所有线程从主存再次获取a的值,这样a改变了,那么其他线程在使用a的值的时候,是最新的值,而不是缓存下来的值
9.ThreadLocal
ThreadLocal 本地数据接口,以当前Thread 为key,value是我们set的值。相当于为每个thread保存一个副本。这样下次这个线程访问的就是自己保存的副本,保证了数据的唯一性,从根源上解决多线程问题
10.Thread.interrupted vs thread.interrupt
image.png这俩个方法都是用来获取前程是否中断的,唯一的不同点就是thread.interrupt不会重置状态。Thread.interrupted会重置状态(重置成false)。所有线程默认是非中断 的。重置的意思是:当一个线程设置成中断,第一次调用Thread.interrupted 将返回true,第二次调用将返回false。
线程池关闭某一个线程java实现:
public static void main(String[] args) {
ThreadPoolExecutor mThreadPoolExecutor = (ThreadPoolExecutor) Executors.newCachedThreadPool();
long t=System.currentTimeMillis();
Future mFuture = mThreadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
while (Thread.interrupted() == false) {
System.out.println("while正在执行");
}
System.out.println("当前线程是否中断:"+Thread.interrupted());//最后打印的为false,清除了之前的状态
}
});
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
mFuture.cancel(true);
System.out.println(System.currentTimeMillis()-t);
}
11 线程wait 和notify/notifyall
对象.wait(): 导致当前的线程等待,直到其他线程调用此对象的notify( ) 方法或 notifyAll( ) 方法
对象.notify():唤醒在此对象锁上等待的单个线程
public static void main(String[] args) {
Object m=new Object();
Thread thread1=new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <20; i++) {
if (i==10){
synchronized (m){
try {
m.wait();//假如不要m,直接调用就会报异常
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}else {
System.out.println(i);
}
}
}
});
thread1.start();
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("唤醒线程");
synchronized (m){
m.notify();
}
}
注意点:
含有wait 和notify 代码块都需要用synchronized 修饰。并且还要一样。
12 Thread.sleep 和 Object.wait 区别
在一个加锁了的代码块中执行Thread.sleep,不会释放锁。
在一个加了锁的代码块中执行wait,会释放锁