读前说明:

由于是多线程编程,CPU调度线程的随机性,运行结果,每次都有可能不同,故在此没有截图程序的运行结果。读者可自行运行程序,检验结果。
本文着重介绍java多线程编程的基础---耐心看完,就会有满满的收获

多线程

1.如果程序只有一条执行路径(main),那么该程序就是单线程程序。
2.如果程序有多条执行路径,那么该程序就是多线程程序。
image.png
3.线程是依赖进程,而存在的。
4.进程:就是正在运行的程序。
5.多进程的意义:提高CPU的使用率。
每一个进程都有自己的内存空间和系统资源。
image.png
6.线程是程序的执行单元,是CPU调度的基本单位
7.多线程的意义:是为了提高应用程序的使用率。有更高的机率抢到CPU的执行权。
8.线程的执行有随机性。(人不会知道,CPU调度将要调度的是哪个线程)
image.png
9.并行:逻辑上同时发生,在某一个时间段内,同时执行多个程序。
(多个CPU,多个任务,一个CPU执行一个任务,多个CPU同时执行)
10.并发:物理上同时发生,在某一个时间点上,同时执行多个程序。
(一个CPU,多个任务)并发是多个线程被(一个)CPU轮流切换着执行
image.png
11.Java程序的运行原理:
由java命令启动JVM,JVM启动就相当于启动了一个进程(操作系统windows层面看),
接着由该进程创建一个主线程去调用main方法。

思考题:jvm虚拟机的启动是单线程的还是多线程的?
多线程
原因是:垃圾回收线程也要先启动,否则很容易出现内存溢出。
现在的垃圾回收线程加上前面的主线程,最少启动了两个线程,所以,jvm 的启动其实是多线程的。
12.java是不能直接调用系统功能的(需要使用C/C++语言)。
进程是由系统创建的,所以我们应该去调用系统功能创建一个进程(C/C++语言实现)
image.png
image.png
13.run() 与 start()
直接调用run()方法,效果就是单线程的效果。相当于一个普通的方法。
调用start()方法:首先启动线程,然后去调用该线程的run()方法。
想要启动几个线程,就需要创建几个线程对象。对象.start()
14.写在run()方法中的代码才能被线程执行。
使用多线程的一个重要原因是:有些代码的执行时间是比较耗时的
获取当前正在执行的线程对象:Thread.currentThread();
image.png
image.png
image.png
image.png
image.png
15.调度线程: java使用的是抢占式调度模型。
优先级高的线程优先获取CPU的时间片
image.png
image.png
image.png
16.线程的加入---join() 等待当前线程结束,再执行其他线程
image.png
17.线程的礼让---yeild()方法
image.png
image.png
18.设置守护线程: setDaemon)(boolean on)
将该线程标记为守护线程或用户线程
当正在运行的线程都是守护 线程时,Java 虚拟机退出。
该方法必须 在启动线程前调用。
image.png
19.线程的停止stop()与中断interrupt()
image.png
image.png
image.png
如果线程在调用 [Object] 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int)方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException
20.线程生命周期:执行资格与执行权
新建 就绪 运行 死亡 阻塞
image.png

  1. 实现Runnable接口

多个人抢CPU的执行权去干自己的活。
多个人抢CPU的执行权去干共同的活。---线程安全问题
image.png
image.png
image.png
低耦合的设计思想--面向对象的设计思想
卖票问题:
image.png
22.静态的变量属于类(全局唯一),因此可以在对象间共享(全局共享)
image.png
runnable更适合将线程与数据、代码分离
image.png
image.png
23.
image.png
卖同票:
image.png
image.png
image.png
如何解决线程安全问题:
image.png
image.png
A和B的问题我们改变不了,我们只能想办法去把C改变一下
思想:
把多条语句操作共享数据的代码宝成一个整体让某个线程在执行的时候,别人不能来执行。
问题是我们不知道怎么包啊?Java给我们提供了;同步机制。
同步代码块:
image.png
多个线程必须是同一把所
image.png
image.png
image.png
说明:
image.png
image.png
同步代码块的锁对象是任意的,多线程的锁对象必须是同一个
同步方法的锁对象是this,本类对象。
静态方法的锁对象是本类的字节码对象。
image.png
Synchronized关键字放在权限修饰符后。public synchronized void 方法名(){}

Collections 集合工具类(以下三个方法均为静态方法)

  1. synchronizedList
    返回指定列表支持的同步(线程安全的)列表。
  2. synchronizedMap
    返回由指定映射支持的同步(线程安全的)映射。
  3. synchronizedSet
    返回指定 set 支持的同步(线程安全的)set。
    image.png

线程安全的类StringBuffer、Vector、HashTable
image.png
Lock是一个接口
image.png
image.png
image.png
image.png
image.png
image.png

public class MyThread {
        public static final MyThread my1=new MyThread();
        public static final MyThread my2=new MyThread();
}
public class DieLock extends Thread{
    private boolean flag;
     public DieLock(boolean flag) {
         this.flag=flag;
     }
        
    @Override
    public void run() {
        while(true) {
            if(flag) {
                synchronized(MyThread.my1) {
                    System.out.println("if 1");
                    synchronized(MyThread.my2) {
                        System.out.println("if 2");
                    }
                }
            }else {
                synchronized(MyThread.my2) {
                    System.out.println("else 1");
                    synchronized(MyThread.my1) {
                        System.out.println("else 2");
                    }
                }
            }
        }
    }
}
public class DieLockTest {

    public static void main(String[] args) {
        DieLock t1=new DieLock(true);
        DieLock t2=new DieLock(false);
        t1.start();
        t2.start();
    }

}

image.png

生产者与消费者问题:
image.png
image.png
特别注意:生产者和消费者一定要针对同一个资源(对象)
如何来控制是同一个资源呢?(重要)
保证生产者和消费者的操作是对同一个资源。可以将这个资源传给生产者,和消费者。此时他们就可以对这个资源进行操作,且能保证操作的就是同一个资源。如何实现?通过构造方法可以实现(含参构造)。
代码示例:
image.png
image.png
image.png
image.png

public class Student {
    String name;
    int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
public class SetStudent implements Runnable {
    Student student;
    public SetStudent(Student student) {
        this.student = student;
    }
    
    @Override
    public void run() {
        student.setName("wxz");
        student.setAge(20);

    }
}
public class GetStudent implements Runnable {
    Student student;
    public GetStudent(Student student) {
        this.student = student;
    }
    @Override
    public void run() {

        System.out.println(student.getName()+"---"+student.getAge());
    }
}
public class StudentTest {
    public static void main(String[] args) {
        Student s1=new Student();
        SetStudent s=new SetStudent(s1);
        GetStudent g = new GetStudent(s1);
        Thread t1=new Thread(s);
        Thread t2=new Thread(g);
        t2.start();
        t1.start();
    
    }
}

一种结果:
image.png
一种结果:
image.png
结果有很多种(线程调度的随机性),就不一一列举了。
image.png

不同种类线程加的锁必须是同一把锁。即锁对象必须是同一个,本例中的共享资源(student对象就是同一个对象,因此可以作为锁对象)。

image.png
image.png
image.png
线程的等待唤醒机制
image.png
image.png
s.wait(); //t2就等待了。此时立即释放锁,将来是从这里醒过来的。
s.notify();//唤醒并不表示立马可以执行,必须还得抢CPU的执行权。

public class Student {
    String name;
    int age;
    boolean flag;//默认值为false
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    
}
public class SetStudent implements Runnable {
    Student student;
    private int x=0;
    public SetStudent(Student student) {
        this.student = student;
    }

    @Override
    public void run() {

        while(true){
            synchronized(student) {
                if(student.flag) {
                    try {
                        student.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if(x % 2 == 0) {
                    student.setName("wxz");
                    student.setAge(20);
                    //System.out.println("生产");

                }else {
                    student.setName("summer");
                    student.setAge(10);
                    //System.out.println("生产");
                }
                x++;
                student.flag=true;                
                student.notify();//唤醒并不表示立马可以执行,必须还得抢CPU的执行权
            }
        }

    }
}
flag标志的作用:表示是否有生产的资源, 若有便可以通知消费者消费。
若没有便可通知生产者生产。
public class GetStudent implements Runnable {
    Student student;
    public GetStudent(Student student) {
        this.student = student;
    }
    @Override
    public void run() {
        while(true){
            synchronized(student) {
                if(!student.flag) {
                    try {                        
                        student.wait();//t2等待,立即释放锁,将来是从这里醒过来的
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //System.out.println("消费");
                System.out.println(student.getName()+"---"+student.getAge());                
                student.flag=false;
                student.notify();
                
            }
        }
    }
}
public class StudentTest {

    public static void main(String[] args) {
        Student s1=new Student();
        SetStudent s=new SetStudent(s1);
        GetStudent g = new GetStudent(s1);
        Thread t1=new Thread(s);
        Thread t2=new Thread(g);
        t2.start();
        t1.start();
    }
}

image.png
image.png

生产者与消费者问题的最终版代码

把Student的成员变量给私有化,
把设置和获取的操作封装成了功能,并加了同步。
设置或者获取的线程里面之需要调用方法即可。
package com.study;

public class Student {
    private String name;
    private int age;
    private boolean flag;//默认值为false

    // 方法锁对象为默认的this
    // 那个对象调用set方法,那么this指得就是那个对象。由于这里的set和get方法都是由student对象来调用的,
    // 故这里的this指的是student对象,因此锁对象一样。能解决线程安全问题。
    public synchronized void set(String name,int age) {
        
        if(this.flag) {
            //System.out.println(this+"=========================================");
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.name=name;
        this.age=age;
        this.flag=true;
        this.notify();
    }

    public synchronized void get() {
        if(!this.flag) {
            //System.out.println(this+"+++++++++++++++++++++++++++++++++++++++++");
            try {
                this.wait();
            } catch (InterruptedException e) {
                
            }
        }
        System.out.println(this.name+"---"+this.age);
        this.flag=false;
        this.notify();    
    }    
}
package com.study;

public class SetStudent implements Runnable {
    Student student;
    private int x=0;
    public SetStudent(Student student) {
        this.student = student;
    }

    @Override
    public void run() {

        while(true){
            if(x%2==0) {
                student.set("wxz",10);
            }else {
                student.set("summer",20);
            }
            x++;
        }
    }
}
package com.study;

public class GetStudent implements Runnable {
    Student student;
    public GetStudent(Student student) {
        this.student = student;
    }
    @Override
    public void run() {
        while(true){            
            student.get();                            
        }    
    }
}
package com.study;

public class StudentTest {

    public static void main(String[] args) {
        Student s1=new Student();

        SetStudent s=new SetStudent(s1);
        GetStudent g = new GetStudent(s1);
        
        Thread t1=new Thread(s);
        Thread t2=new Thread(g);
        t2.start();
        t1.start();
    }
}

线程池

image.png
image.png
image.png
image.png

package com.executors;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
// 线程池有五个线程,来了六个任务,只能等一个线程干完,还了链接之后才可以开始最后一个任务。

public class ExecutorsTest {

    public static void main(String[] args) {
        ExecutorService newFixedThreadPool = Executors.newFixedThreadPool(5);
        newFixedThreadPool.submit(new Task());
        newFixedThreadPool.submit(new Task());
        newFixedThreadPool.submit(new Task());
        newFixedThreadPool.submit(new Task());
        newFixedThreadPool.submit(new Task());
        newFixedThreadPool.submit(new Task());
        newFixedThreadPool.shutdown();//关闭线程池
    }

}
package com.executors;

public class Task implements Runnable {

    @Override
    public void run() {
        for (int i = 0;i <10 ;i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }

}

定时器

image.png
image.png
image.png

package com.timer;

import java.util.Timer;

public class TimerTest {

    public static void main(String[] args) {
        Timer timer=new Timer();
        timer.schedule(new MyTask(timer), 5000);
    }

}
package com.timer;

import java.util.Timer;
import java.util.TimerTask;

public class MyTask extends TimerTask{
    
    private Timer timer;
    public MyTask() {
        
    }
    public MyTask(Timer timer) {
        this.timer=timer;
    }
    @Override
    public void run() {
        System.out.println("到点了");    
        timer.cancel();
    }

}

image.png

package com.timer;

import java.util.Timer;

public class TimerTest {

    public static void main(String[] args) {
        Timer timer=new Timer();
        timer.schedule(new MyTask(), 1000,1000);
    }

}
package com.timer;

import java.util.Date;
import java.util.TimerTask;

public class MyTask extends TimerTask{

    @Override
    public void run() {
        System.out.println(new Date());    
    }

}

image.png

定时删除指定目录下的资源:

package com.timer;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;

public class DelectTest {

    public static void main(String[] args) throws ParseException {
        Timer timer=new Timer();
        String date="2020-12-18 16:48:00";
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date time=simpleDateFormat.parse(date);
        timer.schedule(new DeleteFolder(), time);
    }

}
package com.timer;

import java.io.File;
import java.util.TimerTask;

public class DeleteFolder extends TimerTask{

    @Override
    public void run() {
        File file = new File("E:/test");
        deleteFolder(file);
    }

    private void deleteFolder(File file) {
        if(file != null) {
            File[] listFiles = file.listFiles();
            for(File f:listFiles) {
                if (f.isDirectory()) {
                    deleteFolder(f);
                }else {
                    System.out.println(f.getName());
                    f.delete();                    
                }
                
            }
            file.delete();
        }    
    }
        
}

image.png

常见面试题:

image.png
image.png


summer
30 声望3 粉丝

这一路我披荆斩棘,只为遇见你