JAVA常用知识总结(九)——线程
sleep和wait的区别?
sleep()来自Thread类,和wait()来自Object类.调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁
sleep()睡眠后不出让系统资源,wait让其他线程可以占用CPU
sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒.而wait()需要配合notify()或者notifyAll()使用
notify()和notifyAll()有什么区别?
如果线程调用了对象的 wait()方法,那么线程便会处于该对象的等待池中,等待池中的线程不会去竞争该对象的锁。
当有线程调用了对象的 notifyAll()方法(唤醒所有 wait 线程)或 notify()方法(只随机唤醒一个 wait 线程),被唤醒的的线程便会进入该对象的锁池中,锁池中的线程会去竞争该对象锁。也就是说,调用了notify后只要一个线程会由等待池进入锁池,而notifyAll会将该对象等待池内的所有线程移动到锁池中,等待锁竞争。
优先级高的线程竞争到对象锁的概率大,假若某线程没有竞争到该对象锁,它还会留在锁池中,唯有线程再次调用 wait()方法,它才会重新回到等待池中。而竞争到对象锁的线程则继续往下执行,直到执行完了 synchronized 代码块,它会释放掉该对象锁,这时锁池中的线程会继续竞争该对象锁。
java创建线程的三种方式及其对比
1、继承Thread类创建线程类
(1)定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
(2)创建Thread子类的实例,即创建了线程对象。
(3)调用线程对象的start()方法来启动该线程。
2、通过Runnable接口创建线程类
(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
(2)创建 Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
(3)调用线程对象的start()方法来启动该线程。
3、通过Callable和Future创建线程
(1)创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
public interface Callable
{
V call() throws Exception;
}(2)创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。(FutureTask是一个包装器,它通过接受Callable来创建,它同时实现了Future和Runnable接口。)
(3)使用FutureTask对象作为Thread对象的target创建并启动新线程。
(4)调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
package com.thread; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; public class CallableThreadTest implements Callable<Integer>
{ public static void main(String[] args)
{
CallableThreadTest ctt = new CallableThreadTest();
FutureTask<Integer> ft = new FutureTask<>(ctt);
for(int i = 0;i < 100;i++)
{
System.out.println(Thread.currentThread().getName()+" 的循环变量i的值"+i);
if(i==20)
{
new Thread(ft,"有返回值的线程").start();
}
}
try
{
System.out.println("子线程的返回值:"+ft.get());
} catch (InterruptedException e)
{
e.printStackTrace();
} catch (ExecutionException e)
{
e.printStackTrace();
} } @Override
public Integer call() throws Exception
{
int i = 0;
for(;i<100;i++)
{
System.out.println(Thread.currentThread().getName()+" "+i);
}
return i;
} }
创建线程的三种方式的对比
1、采用实现Runnable、Callable接口的方式创建多线程时,
优势是:
线程类只是实现了Runnable接口或Callable接口,还可以继承其他类。
在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU、代码和数据分开,形成清晰的模型,较好地体现了面向对象的思想。
劣势是:
编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法。
2、使用继承Thread类的方式创建多线程时,
优势是:
编写简单,如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
劣势是:
线程类已经继承了Thread类,所以不能再继承其他父类。
3、Runnable和Callable的区别
(1) Callable规定(重写)的方法是call(),Runnable规定(重写)的方法是run()。
(2) Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
(3) call方法可以抛出异常,run方法不可以。
(4) 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
Thread.sleep(0)的作用是什么?
由于Java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到CPU控制权的情况,为了让某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,当前线程暂时放弃cpu,这也是平衡CPU控制权的一种操作。
什么是乐观锁和悲观锁
乐观锁:乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。
悲观锁:悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了。
线程池的好处和分类?
线程池的好处:
1:通过重复利用已创建的线程,减少在创建和销毁线程上所花的时间以及系统资源的开销。
2:提高响应速度,当任务到达时,任务可以不需要等到线程创建就可以立即执行。
3:提高线程的可管理性,使用线程池可以对线程进行统一的分配和监控。
4:如果不使用线程池,有可能造成系统创建大量线程而导致消耗完系统内存。
线程池的注意事项:
1:线程池的大小:多线程应用并非线程越多越好。需要根据系统运行的硬件环境以及应用本身的特点决定线程池的大小。一般来说,如果代码结构合理,线程数与cpu数量相适合即可。如果线程运行时可能出现阻塞现象,可相应增加池的大小、如果有必要可采用自适应算法来动态调整线程池的大小。以提高cpu的有效利用率和系统的整体性能。
2:并发错误:多线程应用要特别注意并发错误,要从逻辑上保证程序的正确性,注意避免死锁现象的发生。
3:线程泄露:这是线程池应用中的一个严重的问题、当任务执行完毕而线程没能返回池中就会发生线程泄露现象。
其实四种线程池都是 ThreadPoolExecutor ,只是创建参数不同
newSingleThreadExecutor
创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
newFixedThreadPool
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
newCachedThreadPool
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
newScheduledThreadPool
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
package com.king.lu.Demo; import java.util.Calendar;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; public class MutiThread {
private volatile static int a=0; //https://mp.weixin.qq.com/s/DZkGRTan2qSzJoDAx7QJagprivate final static Integer INDEX = 1000000000;
//对于静态方法,由于此时对象还未生成,所以只能采用类锁;
private static void add(){
synchronized (MutiThread.class) {
a++;
}
} public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
long startTime = System.currentTimeMillis(); //获取开始时间
// for(int i=0;i<INDEX;i++){
//// System.out.println(Thread.currentThread().getName());
// add();
// } // for(int i=0;i<10000;i++){
// new Thread(new Runnable() {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName());
// add();
// }
// }).start();
// } ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
singleThreadExecutor.execute(new Runnable() {
public void run() {
for(int i=0;i<INDEX;i++){
// System.out.println(Thread.currentThread().getName());
add();
}
}
}); // ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
// scheduledThreadPool.schedule(new Runnable() {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName()+"delay 3 seconds");
// }
// },3, TimeUnit.SECONDS);
//
// scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
// @Override
// public void run() {
// System.out.println(Thread.currentThread().getName()+"delay 3 seconds");
// }
// }, 1,1, TimeUnit.SECONDS); // ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
// cachedThreadPool.execute(new Runnable() {
// public void run() {
// for(int i=0;i<INDEX;i++){
// // System.out.println(Thread.currentThread().getName());
// add();
// }
// }
// }); // ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
// fixedThreadPool.execute(new Runnable() {
// public void run() {
// for(int i=0;i<INDEX;i++){
// // System.out.println(Thread.currentThread().getName());
// add();
// }
//
// }
// });
////
////
singleThreadExecutor.shutdown();
singleThreadExecutor.awaitTermination(1, TimeUnit.HOURS);
while(true){
if(singleThreadExecutor.isTerminated()){
break;
}
} // while(Thread.activeCount()>1){
// System.out.println(Thread.activeCount()+","+Thread.currentThread().getName());
// Thread.yield();
// } //保证前面的线程都执行完
long endTime = System.currentTimeMillis(); //获取结束时间
System.out.println("程序运行时间:" + (endTime - startTime) + "ms"); //输出程序运行时间
System.out.println(a); } }
什么叫线程安全?servlet是线程安全吗?
线程安全就是说多线程访问同一代码,不会产生不确定的结果。
在多线程环境中,当各线程不共享数据的时候,即都是私有(private)成员,那么一定是线程安全的。但这种情况并不多见,在多数情况下需要共享数据,这时就需要进行适当的同步控制了。
线程安全一般都涉及到synchronized, 就是一段代码同时只能有一个线程来操作 不然中间过程可能会产生不可预制的结果。
如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的。
什么是Daemon线程?它有什么意义?
Java语言自己可以创建两种进程“用户线程”和“守护线程”
用户线程:就是我们平时创建的普通线程.
守护线程:主要是用来服务用户线程.
Daemon就是守护线程,他的意义是:
只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。
SynchronizedMap和ConcurrentHashMap有什么区别?
SynchronizedMap()和Hashtable一样,实现上在调用map所有方法时,都对整个map进行同步。而ConcurrentHashMap的实现却更加精细,它对map中的所有桶加了锁。所以,只要有一个线程访问map,其他线程就无法进入map,而如果一个线程在访问ConcurrentHashMap某个桶时,其他线程,仍然可以对map执行某些操作。
所以,ConcurrentHashMap在性能以及安全性方面,明显比Collections.synchronizedMap()更加有优势。同时,同步操作精确控制到桶,这样,即使在遍历map时,如果其他线程试图对map进行数据修改,也不会抛出ConcurrentModificationException。
JAVA常用知识总结(九)——线程的更多相关文章
- java常用的几种线程池比较
1. 为什么使用线程池 诸如 Web 服务器.数据库服务器.文件服务器或邮件服务器之类的许多服务器应用程序都面向处理来自某些远程来源的大量短小的任务.请求以某种方式到达服务器,这种方式可能是通过网络协 ...
- Java基础知识陷阱(九)
本文发表于本人博客. 今天我来说说关于JAVA多线程知识,有错误请指出.大家都知道JAVA在服务端上处理也有很大优势,很多公司也有在服务器跑JAVA进程,这说明JAVA在处理这个多线程以及并发下也有一 ...
- Java常用的几种线程池
常用的几种线程池 5.1 newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程. 这种类型的线程池特点是: 工作线程的创 ...
- JAVA基础知识之多线程——线程通信
传统的线程通信 Object提供了三个方法wait(), notify(), notifyAll()在线程之间进行通信,以此来解决线程间执行顺序等问题. wait():释放当前线程的同步监视控制器,并 ...
- JAVA常用知识总结(七)——Spring
如果一个接口有2个不同的实现, 如何Autowire某一个指定的实现? 1.通过增加@Qualifier(实现类的名字): @Autowired @Qualifier("GirlStuden ...
- JAVA常用知识总结(六)——Mybatis
为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里? Hibernate属于全自动ORM映射工具,使用Hibernate查询关联对象或者关联集合对象时,可以根据对象关系模型直接获取, ...
- JAVA常用知识总结(三)——JAVA虚拟机
先附一张JAVA虚拟机内存结构图: 其中JAVA虚拟机的线程问题<为什么JAVA虚拟机分为线程共享和非线程共享?>一文中已经有详细介绍,本文从面试中常问的一些JAVA虚拟机问题出发,主要从 ...
- JAVA基础知识之多线程——线程池
线程池概念 操作系统或者JVM创建一个线程以及销毁一个线程都需要消耗CPU资源,如果创建或者销毁线程的消耗源远远小于执行一个线程的消耗,则可以忽略不计,但是基本相等或者大于执行线程的消耗,而且需要创建 ...
- JAVA基础知识之多线程——线程组和未处理异常
线程组 Java中的ThreadGroup类表示线程组,在创建新线程时,可以通过构造函数Thread(group...)来指定线程组. 线程组具有以下特征 如果没有显式指定线程组,则新线程属于默认线程 ...
随机推荐
- 侧方位停车想一次过,掌握边线30cm很重要!
侧方位停车要想一次过关,关键在于保持车身距离库边线30cm左右的距离.但是,往往有很多学员掌控不好这个距离,导致倒库时压线.那么,如何找准这个30cm呢?下面,小编就来教大家方法,赶紧学习吧! 侧方位 ...
- html5--5-4 绘制矩形
html5--5-4 绘制矩形 学习要点 掌握绘制矩形的方法:strkeRect()/fillRect() 掌握绘制路径的 beginPath()和closePath() 矩形的绘制方法 rect(x ...
- IntelliJ IDEA 2018 设置代码提示对大小写不敏感
setting->Editor->General->Code Completion取消勾选Match case
- Linux服务:使用Supervisor管理进程
一.简介 由于基本每个公司都会用到supervisor这个进程管理工具,这里简单阐述一下. Supervisor (http://supervisord.org) 是一个用Python写Linux下的 ...
- bzoj 2836 魔法树——树链剖分
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2836 树剖裸题.然而WA.RE了好久…… 原来是跳 top 的那个地方! top 不相等的时 ...
- bzoj 3489 A simple rmq problem——主席树套线段树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3489 题解:http://www.itdaan.com/blog/2017/11/24/9b ...
- adb 调试命令
一.抓log 1. cat /proc/kmsg:抓kernel log(串口log) 2. 进入工程模式:adb shell am start -n com.mediatek.engineermo ...
- POJ3261(后缀数组+2分枚举)
Milk Patterns Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 12972 Accepted: 5769 Ca ...
- SSIS 增量更新
本文转自 http://sqlblog.com/blogs/andy_leonard/archive/2007/07/09/ssis-design-pattern-incremental-loads. ...
- ASP.NET Core会议管理平台实战_汇总贴
ASP.NET Core会议管理平台实战 课程地址:https://ke.qq.com/course/389673?from=800004097#term_id=100464670 ASP.NET C ...