多线程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程相关的知识,希望对你有一定的参考价值。

多线程

进程概述

进程就是正在运行的程序,是系统进行资源分配和调用的独立单位。
每一个进程都有它自己的内存空间和系统资源。

多进程的意义

单进程计算机只能做一件事情。而我们现在的计算机都可以一边玩游戏(游戏进程),一边听音乐(音乐进程),
所以我们常见的操作系统都是多进程操作系统。比如:Windows,Mac和Linux等,能在同一个时间段内执行多个任务。
对于单核计算机来讲,游戏进程和音乐进程是同时运行的吗?不是。
因为CPU在某个时间点上只能做一件事情,计算机是在游戏进程和音乐进程间做着频繁切换,且切换速度很快,
所以,我们感觉游戏和音乐在同时进行,其实并不是同时执行的。多进程的作用不是提高执行速度,而是提高CPU的使用率。

什么是线程

在一个进程内部又可以执行多个任务,而这每一个任务我们就可以看成是一个线程。是程序使用CPU的基本单位。

多线程有什么意义

多线程的作用不是提高执行速度,而是为了提高应用程序的使用率。
怎么理解?
我们程序在运行的使用,都是在抢CPU的时间片(执行权),如果是多线程的程序,那么在抢到
CPU的执行权的概率应该比较单线程程序抢到的概率要大.那么也就是说,CPU在多线程程序
中执行的时间要比单线程多,所以就提高了程序的使用率.但是即使是多线程程序,那么他们
中的哪个线程能抢占到CPU的资源呢,这个是不确定的,所以多线程具有随机性.

并发和并行

并发是逻辑上同时发生,指在某一个时间内同时运行多个程序。
并行是物理上同时发生,指在某一个时间点同时运行多个程序。

什么是并发

指应用能够交替执行不同的任务, 其实并发有点类似于多线程的原理, 多线程并非是如果你开两个线程同时执行多个任务, 执行, 就是在你几乎不可能察觉到的速度不断去切换这两个任务, 已达到"同时执行效果", 其实并不是的, 只是计算机的速度太快, 我们无法察觉到而已. 就类似于你, 吃一口饭喝一口水, 以正常速度来看, 完全能够看的出来, 当你把这个过程以n倍速度执行时..可以想象一下

什么是并行

指应用能够同时执行不同的任务, 例:吃饭的时候可以边吃饭边打电话, 这两件事情可以同时执行

多线程

Java程序运行原理

ava命令会启动java虚拟机,启动JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。
所以 main方法运行在主线程中。

JVM的启动是多线程的吗?

JVM启动至少启动了垃圾回收线程和主线程,所以是多线程的。

如何实现多线程

由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。
而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。
但是Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。
但是呢?Java可以去调用C/C++写好的程序来实现多线程程序。
由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,
然后提供一些类供我们使用。我们就可以实现多线程程序了。
参考Thread类

多线程程序实现的方式1

我们启动线程使用不是run方法,而应该是start方法.使该线程开始执行;
Java 虚拟机调用该线程的 run 方法。

 为什么要重写run方法?

这个类是一个线程类,那么在这个类中我们可不可以写一些其他的方法呢?
我们可以在写其他的方法,那么其他方法中封装的代码都是需要被我们线程执行的吗? 不一定那么也就是run方法中封装应该是必须被线程执行的代码.
run方法中的代码的书写原则: 一般是比较耗时的代码

列:

public static void main(String[] args) {
//在Java中如何开启一个线程,利用 Thread 这个线程类
//线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
MyThread th1 = new MyThread();
th1.setName("刘亦菲");
th1.setPriority(1);
th1.start();//开启线程的方法
MyThread th2 = new MyThread();
th2.setPriority(Thread.MAX_PRIORITY);
th2.setName("林青霞");
th2.start();
//Java中多个线程,执行是随机性的,因为我Java采用的线程调度模型是抢占式调度,线程优先级高的优先使用CPU的执行权
//优先级一样,就是随机抢占

}

public class MyThread extends Thread {br/>@Override
public void run() { //需要线程来执行的方法
//一般run方法里面写的就是耗时操作
for (int i = 0; i < 100; i++) {
//System.out.println(this.getName()+"=="+i);

        System.out.println(Thread.currentThread().getName()+"===" + i);

    }
}

}

线程的执行

假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,
线程只有得到 CPU时间片,也就是使用权,才可以执行指令。

线程调度的两种模式

分时调度模型

所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间片

抢占式调度模型

优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。 Java使用的是抢占式调度模型。

设置线程的优先级

public final int getPriority() //获取线程的优先级
public final void setPriority(int newPriority)//设置线程的优先级

线程休眠

线程休眠: public static void sleep(long millis) 线程休眠

列:

public static void main(String[] args) throws InterruptedException {

    System.out.println("这是一段广告");
    Thread.sleep(5000);
    System.out.println("bbbbb");
    //Thread(String name) 分配新的 Thread 对象。
    MyThread th1 = new MyThread("林青霞");
    th1.start();
}

public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() { //需要线程来执行的方法
    //一般run方法里面写的就是耗时操作
    for (int i = 0; i < 100; i++) {
        try {
            Thread.sleep(2000);//让线程休眠
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //System.out.println(this.getName()+"=="+i);
        System.out.println(Thread.currentThread().getName() + "===" + i);

    }
}

}

加入线程

加入线程: public final void join()
意思就是: 等待该线程执行完毕了以后,其他线程才能再次执行
注意事项: 在线程启动之后,在调用方法

列:

public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread th1 = new MyThread("刘备");
MyThread th2 = new MyThread("关羽");
MyThread th3 = new MyThread("张飞");

    //join()方法可以将多个线程并发的执行,转换成串行
    //A:
    //加入线程:
    //public final void join ()
    //意思就是:
    //等待该线程执行完毕了以后, 其他线程才能再次执行
    //注意事项:
    //在线程启动之后, 在调用方法
    th1.start();
    th1.join();
    th2.start();
    th2.join();
    th3.start();
}

}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() { //需要线程来执行的方法
    //一般run方法里面写的就是耗时操作
    for (int i = 0; i < 100; i++) {

        System.out.println(Thread.currentThread().getName() + "===" + i);

    }
}

}

礼让线程

礼让线程: public static void yield(): 暂停当前正在执行的线程对象,并执行其他线程。

列:

public class MyTest {
public static void main(String[] args) {
MyThread th1 = new MyThread("刘备");
MyThread th2 = new MyThread("关羽");
th1.start();
th2.start();

}

}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() { //需要线程来执行的方法
    //一般run方法里面写的就是耗时操作
    for (int i = 0; i < 100; i++) {
        Thread.yield();//线程礼让
        System.out.println(Thread.currentThread().getName() + "===" + i);

    }
}

}

守护线程

守护线程: public final void setDaemon(boolean on):
将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
该方法必须在启动线程前调用。

列:

public class MyTest {
public static void main(String[] args) {
Thread.currentThread().setName("刘备");
System.out.println("主线程开始了");
System.out.println("主线程开始了");
System.out.println("主线程开始了");
System.out.println("主线程开始了");

    //A:
    //守护线程:
    //public final void setDaemon ( boolean on):
    //将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。
    //该方法必须在启动线程前调用。
    //当用户线程,执行完之后,那么守护线程,必须里面死亡
    MyThread th1 = new MyThread("张飞");
    MyThread th2 = new MyThread("关羽");
    //设置为守护线程 该方法必须在启动线程前调用。
    th1.setDaemon(true);
    th2.setDaemon(true);
    th1.start();
    th2.start();

    System.out.println("主线程结束了");
    System.out.println("主线程结束了");
    System.out.println("主线程结束了");
    System.out.println("主线程结束了");
    System.out.println("主线程结束了");

}

}
public class MyThread extends Thread {
public MyThread(String name) {
super(name);
}

@Override
public void run() { //需要线程来执行的方法
    //一般run方法里面写的就是耗时操作
    for (int i = 0; i < 100; i++) {
        System.out.println(Thread.currentThread().getName() + "===" + i);

    }
}

}

java用户线程和守护线程

  • 1.用户线程和守护线程的区别
    用户线程和守护线程都是线程,区别是Java虚拟机在所有用户线程dead后,程序就会结束。而不管是否还有守护线程还在运行,若守护线程还在运行,则会马上结束。很好理解,守护线程是用来辅助用户线程的,如公司的保安和员工,各司其职,当员工都离开后,保安自然下班了。

线程安全问题产生的原因

JDK5之后的Lock锁的概述和使用

  • Lock锁的概述
    虽然我们可以理解同步代码块和同步方法的锁对象问题,
    但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
    为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
  • Lock和ReentrantLock
    void lock()
    void unlock()
    public class CellRunable implements Runnable {
    static int piao = 100;
    static Object obj = new Object();
    static Lock lock = new ReentrantLock();

    列:

    @Override
    public void run() {

    while (true) {
        lock.lock(); //加锁
        if (piao > 0) {
    
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
            System.out.println(Thread.currentThread().getName() + "正在售卖 " + (piao--) + " 张票");
        }
    
        lock.unlock(); //释放锁
    }

    }

}
public class MyTest {
public static void main(String[] args) {
/ A:
Lock锁的概述
虽然我们可以理解同步代码块和同步方法的锁对象问题,
但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
B:
Lock和ReentrantLock
void lock ()
void unlock ()
/

    CellRunable cellRunable = new CellRunable();
    Thread th1 = new Thread(cellRunable);
    Thread th2 = new Thread(cellRunable);
    Thread th3 = new Thread(cellRunable);
    th1.setName("窗口1");
    th2.setName("窗口2");
    th3.setName("窗口3");
    th1.start();
    th2.start();
    th3.start();
}

}

死锁

列:

public class MyTest3 {
public static void main(String[] args) {
//匿名内部类来开启线程
//方式1
new Thread() {
br/>@Override
public void run() {
System.out.println("线程执行了");
}
}.start();

    //方式2
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("线程执行了");
        }
    }).start();
}

}

定时器

  • 定时器概述
    定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。
    在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编写的开源调度框架。

    列:

    public class MyTest {
    public static void main(String[] args) throws ParseException {
    //定时器:Timer

    // 一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。
    Timer timer = new Timer();
    //可以到时间执行任务
    //void schedule (TimerTask task, Date time)
    //安排在指定的时间执行指定的任务。
    //void schedule (TimerTask task, Date firstTime,long period)
    //安排指定的任务在指定的时间开始进行重复的固定延迟执行。
    //void schedule (TimerTask task,long delay)
    //安排在指定延迟后执行指定的任务。
    //void schedule (TimerTask task,long delay, long period)
    //安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。
    
    //TimerTask由 Timer 安排为一次执行或重复执行的任务。
    //boolean cancel ()
    //取消此计时器任务。
    MyTimerTask myTimerTask = new MyTimerTask(timer);
    //timer.schedule(myTimerTask, 3000); //2秒之后执行定时任务
    // timer.schedule(myTimerTask,2000,1000);//第一次等2秒执行,后面每隔一秒重复执行。
    //myTimerTask.cancel();取消定时任务
    
    //在指定的日期来执行任务
    String str = "2019-05-17 15:45:00";
    Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(str);
    
    timer.schedule(myTimerTask, date);
    
    // timer.cancel();取消定时器

    }
    }
    public class MyTimerTask extends TimerTask {
    Timer timer;
    public MyTimerTask(Timer timer) {
    this.timer=timer;

    }

    @Override
    public void run() {
    System.out.println("砰~~~ 爆炸了");
    //timer.cancel();
    }
    }

以上是关于多线程的主要内容,如果未能解决你的问题,请参考以下文章

线程学习知识点总结

多个请求是多线程吗

python小白学习记录 多线程爬取ts片段

多线程编程

多线程编程

python多线程