线程间的等待唤醒机制
- Object 类中:
- void wait () 在其他线程调用此对象的 notify () 方法或 notifyAll () 方法前,导致当前线程等待。
- void wait (long timeout) 在其他线程调用此对象的 notify () 方法或 notifyAll () 方法,或者超过指定的时间量前,导致当前线程等待。
- void notify () 唤醒在此对象监视器上等待的单个线程。
- void notifyAll () 唤醒在此对象监视器上等待的所有线程。
多线程(内存可见性问题 volatile)
- volatile 解决内存可见性问题
-
一、Java内存模型:
想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的。
Java内存模型规定了所有的变量都存储在主内存中。每条线程中还有自己的工作内存,
线程的工作内存中保存了被该线程所使用到的变量(这些变量是从主内存中拷贝而来)。
线程对变量的所有操作(读取,赋值)都必须在工作内存中进行。
不同线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。 -
二、Java中的可见性:
对于可见性,Java提供了volatile关键字来保证可见性。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。
而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,
当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。
另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,
并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。 -
volatile 关键字:当多个线程进行操作共享数据时,可以保证内存中的数据可见。
相较于 synchronized 是一种较为轻量级的同步策略。volatile 变量,用来确保将变量的更新操作通知到其他线程。
可以将 volatile 看做一个轻量级的锁,但是又与锁有些不同:
对于多线程,不是一种互斥关系
不能保证变量状态的“原子性操作”
-
多线程(CAS 算法)
-
CAS 算法
CAS (Compare-And-Swap) 是一种硬件对并发的支持,针对多处理器
操作而设计的处理器中的一种特殊指令,用于管理对共享数据的并
发访问。
CAS 是一种无锁的非阻塞算法的实现。
CAS 包含了 3 个操作数:
需要读写的内存值 V
进行比较的值 A
拟写入的新值 B
当且仅当 V 的值等于 A 时, CAS 通过原子方式用新值 B 来更新 V 的值,否则不会执行任何操作。
jdk5增加了并发包java.util.concurrent.*,其下面的类使用CAS算法实现了区别于synchronouse同步锁的一种乐观锁。JDK 5之前Java语言是靠synchronized关键字保证同步的,这是一种独占锁,也是是悲观锁。java.util.concurrent.atomic 包下提供了一些原子操作的常用类:
AtomicBoolean 、 AtomicInteger 、 AtomicLong 、 AtomicReference
AtomicIntegerArray 、 AtomicLongArray
AtomicMarkableReference
AtomicReferenceArray
多线程(线程的状态转换图及常见执行情况)
- 执行情况分为:新建 , 就绪 , 运行 , 冻结 , 死亡
(1)新建:线程被创建出来
(2)就绪:具有CPU的执行资格,但是不具有CPU的执行权
(3)运行:具有CPU的执行资格,也具有CPU的执行权
(4)阻塞:不具有CPU的执行资格,也不具有CPU的执行权
(5)死亡:不具有CPU的执行资格,也不具有CPU的执行权 - 线程的状态转换图:
多线程(线程池的概述和使用)
- 线程池概述
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池 - 内置线程池的使用概述
JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法:
(1). public static ExecutorService newCachedThreadPool(): 根据任务的数量来创建线程对应的线程个数
(2). public static ExecutorService newFixedThreadPool(int nThreads): 固定初始化几个线程
(3). public static ExecutorService newSingleThreadExecutor(): 初始化一个线程的线程池
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。它提供了如下方法
(1). Future<?> submit(Runnable task)
(2). Future submit(Callable task)
使用步骤:
创建线程池对象
创建Runnable或Callable实例
提交Runnable或Callable实例
关闭线程池 - 案例演示: 线程池的使用
public class MyCallable implements Callable<Integer> {
int num;
public MyCallable(int num) {
this.num=num;
}
@Override
public Integer call() throws Exception {
int sum=0;
for (int i = 1; i <=num; i++) {
sum += i;
}
return sum;
}
}
------------------------------------------------------------------------------
public class MyTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
MyCallable myCallable = new MyCallable(10);
Future<Integer> f = executorService.submit(myCallable);
Integer integer = f.get();//获取线程返回的结果
System.out.println(integer);
MyCallable myCallable2 = new MyCallable(100);
Future<Integer> f2 = executorService.submit(myCallable2);
Integer integer1 = f2.get();//获取线程返回的结果
System.out.println(integer1);
executorService.shutdown();
}
}
多线程(匿名内部类的方式实现多线程程序)
- 匿名内部类的方式实现多线程程序
new Thread(){代码…}.start();
new Thread(new Runnable(){代码…}).start(); - 案例演示:Thread的匿名内部类实现多线程程序
public class Demo {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
int sum=0;
for (int i = 1; i <= 100; i++) {
sum+=i;
}
System.out.println("副线程的任务,1-100的和是:"+sum);
}
}.start();
System.out.println("这是主线程...");
}
}
多线程(定时器的概述和使用)
- 定时器概述
定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。
在Java中,可以通过Timer和TimerTask类来实现定义调度的功能。 - Timer和TimerTask
- Timer:
public Timer()
public void schedule(TimerTask task, long delay)::定时一定毫秒后执行定时任务
public void schedule(TimerTask task,long delay,long period):定时一定毫秒后执行定时任务,并以一定毫秒的周期重复执行该定时任务
public void schedule(TimerTask task, Date time):在指定某一时间,执行该定时任务
public void schedule(TimerTask task, Date firstTime, long period):在指定某一时间,执行该定时任务,并以一定毫秒时间的周期重复执行该任务 - TimerTask:定时任务
public abstract void run():运行定时任务
public boolean cancel():取消定时任务
开发中
Quartz是一个完全由java编写的开源调度框架。
- Timer:
- 案例演示: 定时器的使用
public class MyTimerTask extends TimerTask {
Timer timer;
public MyTimerTask(Timer timer) {
this.timer=timer;
}
@Override
public void run() {
System.out.println("~~~~砰!爆炸了");
timer.cancel();
}
}
-----------------------------------------------------------------------------
public class MyTest {
public static void main(String[] args) {
//定时器 Timer
//一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
Timer timer = new Timer();
// public void schedule (TimerTask task,long delay):
//延迟多少毫秒之后,让定时任务执行
MyTimerTask myTimerTask = new MyTimerTask(timer);
timer.schedule(myTimerTask,2000);
//timer.cancel(); 取消定时器
}
}
多线程(定时器的练习)
- 案例演示
定时任务的多次执行代码体现
定时删除指定的带内容目录
public class MyTimerTask extends TimerTask {
Timer timer;
public MyTimerTask(Timer timer) {
this.timer=timer;
}
@Override
public void run() {
File file = new File("F:\\测试图片");
delFolder(file);
timer.cancel();//取消定时器
}
private void delFolder(File file) {
File[] files = file.listFiles();
for (File f : files) {
if(f.isFile()){
f.delete();
}else{
delFolder(f);
}
}
file.delete();
}
}
设计模式(设计模式的概述和分类)
- 设计模式概述
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编写、代码设计经验的总结。
使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性以及代码的结构更加清晰. - 设计模式分类
创建型模式(创建对象的): 单例模式、抽象工厂模式、建造者模式、工厂模式、原型模式。
行为型模式(对象的功能): 适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
结构型模式(对象的组成): 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
设计模式(简单工厂模式概述和使用)
- 简单工厂模式概述: 又叫静态工厂方法模式,它定义一个具体的工厂类负责创建一些类的实例
- 优点: 使用静态工厂模式的优点是实现责任的分割,该模式的核心是工厂类,工厂类含有必要的选择逻辑,可以决定什么时候创建哪一个产品的实例,而客户端则免去直接创建产品的责任,而仅仅是消费产品。也就是说静态工厂模式在不改变客户端代码的情况可以动态的增加产品,明确了类的职责
- 缺点
这个静态工厂类负责所有对象的创建,如果有新的对象增加,或者某些对象的创建方式不同,就需要不断的修改工厂类,不利于后期的维护 - 案例演示
public abstract class Animal {
abstract void eat();
}
--------------------------------------------------------------------------
public class AnimalFactory {
private AnimalFactory() {
}
public static Animal createAnimal(String name){
if("cat".equals(name)){
return new Cat();
}else if("dog".equals(name)){
return new Dog();
} else if ("tiger".equals(name)) {
return new Tiger();
}else{
return null;
}
}
}
--------------------------------------------------------------------------
public class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
}
--------------------------------------------------------------------------
public class Dog extends Animal{
public void eat(){
System.out.println("狗吃骨头");
}
}
--------------------------------------------------------------------------
public class Tiger extends Animal{
@Override
void eat() {
System.out.println("老虎吃饭");
}
}
--------------------------------------------------------------------------
public class MyTest {
public static void main(String[] args) {
Animal an = AnimalFactory.createAnimal("dog");
an.eat();
an=AnimalFactory.createAnimal("cat");
an.eat();
an=AnimalFactory.createAnimal("tiger");
an.eat();
}
}
设计模式(工厂方法模式的概述和使用)
- 工厂方法模式概述
工厂方法模式中抽象工厂类负责定义创建对象的接口,具体对象的创建工作由继承抽象工厂的具体类实现。 - 优点
客户端不需要在负责对象的创建,从而明确了各个类的职责,如果有新的对象增加,只需要增加一个具体的类和具体的工厂类即可,不影响已有的代码,后期维护容易,增强了系统的扩展性 - 缺点: 需要额外的编写代码,增加了工作量
- 案例演示
public abstract class Animal {
abstract void eat();
}
----------------------------------------------------------------------------
public interface BigAnimalFactory {
Animal createAnimal();
}
----------------------------------------------------------------------------
public class Cat extends Animal {
public void eat() {
System.out.println("猫吃鱼");
}
}
----------------------------------------------------------------------------
public class CatFactory implements BigAnimalFactory{
@Override
public Animal createAnimal() {
return new Cat();
}
}
----------------------------------------------------------------------------
public class Dog extends Animal {
public void eat(){
System.out.println("狗吃骨头");
}
}
----------------------------------------------------------------------------
public class DogFactory implements BigAnimalFactory{
@Override
public Animal createAnimal() {
return new Dog();
}
}
----------------------------------------------------------------------------
public class Tiger extends Animal{
@Override
void eat() {
System.out.println("老虎吃饭");
}
}
----------------------------------------------------------------------------
public class TigerFactory implements BigAnimalFactory{
@Override
public Animal createAnimal() {
return new Tiger();
}
}
----------------------------------------------------------------------------
public class MyTest {
public static void main(String[] args) {
Animal dog = new DogFactory().createAnimal();
dog.eat();
Animal cat = new CatFactory().createAnimal();
cat.eat();
Animal tiger = new TigerFactory().createAnimal();
tiger.eat();
}
}
设计模式(单例模式:懒汉式和饿汉式)
- 案例演示: 单例模式之懒汉式
public class Student {
private static Student student=null;
private Student() {
}
public synchronized static Student getStudent(){
if(student==null){
student=new Student();
}
return student;
}
}
- 案例演示: 单例模式之饿汉式
public class Teacher {
private static Teacher teacher=new Teacher();
private Teacher() {
}
public static Teacher getTeacher(){
return teacher;
}
}
设计模式(单例模式的Java代码体现Runtime类)
- Runtime类概述
每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。
应用程序不能创建自己的 Runtime 类实例。 - 查看Runtime的源码: 发现是单例模式的应用
- 案例演示: public Process exec(String command) //执行Dos 命令
public class MyTest {
public static void main(String[] args) throws IOException, ParseException {
//Runtime 采用单例模式的饿汉式
Runtime runtime = Runtime.getRuntime();
//exec("calc"); 可以执行一些DOS命令
//runtime.exec("calc");
//runtime.exec("mspaint");
//定时关机
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
runtime.exec("shutdown -s -t 0");
} catch (IOException e) {
e.printStackTrace();
}
}
},new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2019-08-01 16:06:00"));
}
}
设计模式(模版设计模式概述和使用)
- 需求: 计算一个for循环执行的时间
- 模版设计模式概述
模版方法模式就是定义一个算法的骨架,而将具体的算法延迟到子类中来实现 - 优点和缺点
a:优点: 使用模版方法模式,在定义算法骨架的同时,可以很灵活的实现具体的算法,满足用户灵活多变的需求
b:缺点: 如果算法骨架有修改的话,则需要修改抽象类 - 案例演示: 模版设计模式的使用
public abstract class CalcTime {
public void testTime() throws IOException {
//测试一个 for循环的耗时
long start = System.currentTimeMillis();
testHaoShi();
long end = System.currentTimeMillis();
System.out.println("耗时"+(end-start)+"毫秒");
}
public abstract void testHaoShi() throws IOException;
}
---------------------------------------------------------------------------
public class MyTestFor extends CalcTime{
@Override
public void testHaoShi() {
for (int i = 0; i < 10000; i++) {
System.out.println(' ');
}
}
}
---------------------------------------------------------------------------
public class MyTestCopyFileTime extends CalcTime{
@Override
public void testHaoShi() throws IOException {
Files.copy(Paths.get("辛晓琪 - 领悟.mp3"), Paths.get("E:\\demo.mp3"), StandardCopyOption.REPLACE_EXISTING);
}
}
---------------------------------------------------------------------------
public class MyTest {
public static void main(String[] args) throws IOException {
CalcTime myTestFor = new MyTestFor();
myTestFor.testTime();
myTestFor=new MyTestCopyFileTime();
myTestFor.testTime();
}
}
设计模式(装饰模式概述和使用)
- 装饰模式概述: 装饰模式就是使用被装饰类的一个子类的实例,在客户端将这个子类的实例交给装饰类。是继承的替代方案
- 优点和缺点
a:优点
使用装饰模式,可以提供比继承更灵活的扩展对象的功能,它可以动态的添加对象的功能,
并且可以随意的组合这些功能。
b:缺点: 正因为可以随意组合,所以就可能出现一些不合理的逻辑。 - 案例演示: 装饰模式的使用
public interface Phone { //手机的接口
void call();
}
--------------------------------------------------------------------------------------------------------------------------------------
public class Iphone implements Phone{
public void call(){
System.out.println("打电话");
}
}
--------------------------------------------------------------------------------------------------------------------------------------
public class BZPhone implements Phone{//他是一个包装类
//你要把需要包装的手机传过来
Phone phone;
public BZPhone(Phone phone) {
this.phone=phone;
}
@Override
public void call() {
this.phone.call();
}
}
--------------------------------------------------------------------------------------------------------------------------------------
public class MusicPhone extends BZPhone {
public MusicPhone(Phone phone) {
super(phone);
}
@Override
public void call() {
super.call();
System.out.println("听歌的功能");
}
}
----------------------------------------------------------------------------------------------------------------------------------------
public class VideoPhone extends BZPhone{
public VideoPhone(Phone phone) {
super(phone);
}
@Override
public void call() {
super.call();
System.out.println("看视频的功能");
}
}
----------------------------------------------------------------------------------------------------------------------------------------
public class MyTest {
public static void main(String[] args) {
Phone iphone = new Iphone();
iphone = new VideoPhone(new MusicPhone(iphone));
iphone.call();
}
}
设计模式(观察者模式概述和使用)
- 观察者 = 订阅者 + 发布者
岗位类 求职者 猎头(注册方法,注销方法,发布方法) - 案例: 找猎头找工作
public class Hunter { //中介
//两个成员变量,一个装求职者,一个装工作岗位
ArrayList<WokerMan> mans=new ArrayList<>();
ArrayList<Job> jobs = new ArrayList<>();
//添加求职者的方法
public void addMan(WokerMan wokerMan){
mans.add(wokerMan);
}
//添加工作岗位
public void addJob(Job job){
jobs.add(job);
//通知
sendJobToMan(job,mans);
}
//发布求职信息的方法
private void sendJobToMan(Job job, ArrayList<WokerMan> mans) {
for (WokerMan man : mans) {
System.out.println(man.name+"你好,有一份"+job.jobName+"薪资"+job.sal+"欢迎你前去面试");
}
}
//注销求职者的方法
public void removeMan(WokerMan man){
mans.remove(man);
}
}
--------------------------------------------------------------------------------------------------------------------------------------
public class Job {//工作岗位
public String jobName;
public double sal;
public Job(String jobName, double sal) {
this.jobName = jobName;
this.sal = sal;
}
}
---------------------------------------------------------------------------------------------------------------------------------------
public class WokerMan { //求职者
public String name;
public WokerMan(String name) {
this.name = name;
}
}
---------------------------------------------------------------------------------------------------------------------------------------
public class MyTest {
public static void main(String[] args) {
WokerMan man1 = new WokerMan("张三");
WokerMan man2 = new WokerMan("李四");
WokerMan man3 = new WokerMan("王五");
Hunter hunter = new Hunter();
hunter.addMan(man1);
hunter.addMan(man2);
hunter.addMan(man3);
Job java = new Job("Java开发工程师", 10000);
Job php = new Job("PHP开发工程师", 20000);
Job web = new Job("WEB开发工程师", 8000);
hunter.addJob(java);
hunter.addJob(php);
hunter.removeMan(man1); //注销一个求职者
hunter.addJob(web);
}
}