Java语言学习高级笔记(一):多线程

本笔记适合在了解Java基础后想要进一步进阶学习的同学,同时包含部分Java经典面试题供同学们深入理解。

多线程:

程序(program):是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。
进程(process):是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程——生命周期。
进程作为资源分配的单位。系统在运行时会为每个进程分配不同的内存区域。
线程(thread):进程可进一步细化为线程,是一个程序内部的一条执行路径。
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小。
一个进程中的多个线程共享堆和方法区,这使线程间通信更加简便、高效,但同时带来安全的隐患。

面试题:
并行与并发的区别?
并行:一个时间点同时执行多个任务。
并发:采用时间片轮流执行多个任务,看似“同时”。

面试题:
创建多线程有4种方式。
解决多线程安全问题(即多线程的同步)有3种方式。

多线程的创建:

方式一
①创建线程类继承于Thread类,重写run()方法。
②创建此类对象,用此对象调用start()方法。

方式二
①创建线程类实现Runnable接口,实现run()方法。
②创建此类对象。
③创建Thread类的对象并将实现Runnable接口的对象作为参数传递到Thread类的构造器中。(Thread相当于代理类,创建的线程类相当于被代理类。)
④用Thread类的对象调用start()方法。
(相比于方式一的优点:①共享数据不用声明为静态。 ②用接口解决了多继承限制的弊端。)

方式三:(JDK5.0新增)
①创建线程类实现Callable接口,实现带有返回值并且可以抛出异常的call()方法。
②创建此类对象。
③创建FutureTask的对象futureTask并将实现Callable接口的对象作为参数传递到FutureTask类的构造器中。
④创建Thread类的对象并将FutureTask类的对象futureTask作为参数传递到Thread类的构造器中。
⑤用Thread类的对象调用start()方法。
⑥用如下方法获取Callable中call方法的返回值。
try {
//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
Object sum = futureTask.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
(相比于方式二的优点:①call()可以有返回值的。 ②call()可以抛出异常,被外面的操作捕获,获取异常的信息。 ③Callable是支持泛型的。)

方式四:(JDK5.0新增)
①提供指定线程数量的线程池。
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
②设置线程池的属性。
System.out.println(service.getClass());
service1.setCorePoolSize(15);//如果poolSize<corePoolSize,新增加一个线程处理新的任务。
//如果poolSize=corePoolSize,新任务会被放入阻塞队列等待。
service1.setMaximumPoolSize(20);//如果阻塞队列的容量达到上限,且这时poolSize<maximumPoolSize,新增线程来处理任务。
//如果阻塞队列满了,且poolSize=maximumPoolSize,那么线程池已经达到极限,会根据饱和策略RejectedExecutionHandler拒绝新的任务。
service1.setKeepAliveTime(略);
③执行指定的线程的操作,需要提供实现Runnable接口或Callable接口实现类的对象。
service.execute(new NumberThread());//适合适用于Runnable
service.submit(Callable callable);//适合使用于Callable
④关闭连接池。
service.shutdown();
(相比于方式三的优点:①提高了响应速度。 ②提高了资源的重用率。 ③便于管理线程。)

多线程的同步:(即解决多线程安全问题的方式)

方式一:同步代码块:
synchronized(同步监视器){
//需要被同步的代码
}
(说明:同步监视器也称为锁,任何一个类的对象都可以充当,但是要求多个线程必须要共用同一把锁)
(注意:使用继承Thread类方法创建的线程的同步监视器可以考虑用“类名.class”充当,此时慎用“this”充当,容易造成多把锁)
(注意:使用实现Runnable接口方法创建的线程的同步监视器可以考虑用“this”充当)

方式二:同步方法:如果操作共享数据的代码完整地声明在一个方法中,不妨将此方法声明为同步方法。
访问权限 synchronized 函数返回值 函数名(函数参数){
//需要被同步的代码
}
(注意:此时同步监视器默认为“this”,对于使用继承Thread类方法创建的线程,可将方法改为static方法,此时同步监视器变为“类名.class”)
方式三:Lock锁:(JDK5.0新增)
第一步:在线程类中实例化ReentrantLock类。(private final ReentrantLock lock = new ReentrantLock(false))
(注:false处可省略,默认为false,其含义是不公平上锁,即非队列型进入,即抢占进入)
第二步:将需要被同步的代码用try-catch包围,并且在之后写finally代码。(若内部没有Error或Exception,则不需要写catch)
第三步:在try内部的需要被同步的代码前调用lock.lock()方法。
第四步:在finally中调用lock.unlock()方法。

多线程的通信:

wait():一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。
notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个线程。
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
(注:①这三个方法必须用在同步代码块或同步方法中。②这三个方法的调用者必须是同步代码块或同步方法中的同步监视器。③这三个方法在Object类中。)

面试题:
wait()和sleep()的异同?
相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。
不同点:①两个方法声明的位置不同:Thread类中声明sleep() , Object类中声明wait()。
②调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中。
③关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘学长丶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值