首页 > 其他分享 >多线程

多线程

时间:2022-12-26 22:55:14浏览次数:39  
标签:Thread list t1 线程 new 多线程 public

多线程

什么是进程?什么是线程?
进程是一个应用程序(1个进程是一个软件)。
线程是一个进程中的执行场景/执行单元。
一个进程可以启动多个线程。

对于java程序来说,当在DOS命令窗口中输入:
java HelloWorld 回车之后。
会先启动JVM,而JVM就是一个进程。
JVM再启动一个主线程调用main方法。
同时再启动一个垃圾回收线程负责看护,回收垃圾。
最起码,现在的java程序中至少有两个线程并发,
一个是垃圾回收线程,一个是执行main方法的主线程。

进程和线程是什么关系?举个例子

​ 阿里巴巴:进程
​ 马云:阿里巴巴的一个线程
​ 童文红:阿里巴巴的一个线程

​ 京东:进程
​ 强东:京东的一个线程
​ 妹妹:京东的一个线程

​ 进程可以看做是现实生活当中的公司。
​ 线程可以看做是公司当中的某个员工。

​ 注意:
​ 进程A和进程B的内存独立不共享。(阿里巴巴和京东资源不会共享的!)
​ 魔兽游戏是一个进程
​ 酷狗音乐是一个进程
​ 这两个进程是独立的,不共享资源。

​ 线程A和线程B呢?
​ 在java语言中:
​ 线程A和线程B,堆内存和方法区内存共享。
​ 但是栈内存独立,一个线程一个栈。

​ 假设启动10个线程,会有10个栈空间,每个栈和每个栈之间,
​ 互不干扰,各自执行各自的,这就是多线程并发。

​ 火车站,可以看做是一个进程。
​ 火车站中的每一个售票窗口可以看做是一个线程。
​ 我在窗口1购票,你可以在窗口2购票,你不需要等我,我也不需要等你。
​ 所以多线程并发可以提高效率。

​ java中之所以有多线程机制,目的就是为了提高程序的处理效率。

思考一个问题:
使用了多线程机制之后,main方法结束,是不是有可能程序也不会结束。
main方法结束只是主线程结束了,主栈空了,其它的栈(线程)可能还在
压栈弹栈。

分析一个问题:对于单核的CPU来说,真的可以做到真正的多线程并发吗?

​ 对于多核的CPU电脑来说,真正的多线程并发是没问题的。
​ 4核CPU表示同一个时间点上,可以真正的有4个进程并发执行。

​ 什么是真正的多线程并发?
​ t1线程执行t1的。
​ t2线程执行t2的。
​ t1不会影响t2,t2也不会影响t1。这叫做真正的多线程并发。

​ 单核的CPU表示只有一个大脑:
​ 不能够做到真正的多线程并发,但是可以做到给人一种“多线程并发”的感觉。
​ 对于单核的CPU来说,在某一个时间点上实际上只能处理一件事情,但是由于
​ CPU的处理速度极快,多个线程之间频繁切换执行,跟人来的感觉是:多个事情
​ 同时在做!!!!!
​ 线程A:播放音乐
​ 线程B:运行魔兽游戏
​ 线程A和线程B频繁切换执行,人类会感觉音乐一直在播放,游戏一直在运行,
​ 给我们的感觉是同时并发的。

​ 电影院采用胶卷播放电影,一个胶卷一个胶卷播放速度达到一定程度之后,
​ 人类的眼睛产生了错觉,感觉是动画的。这说明人类的反应速度很慢,就像
​ 一根钢针扎到手上,到最终感觉到疼,这个过程是需要“很长的”时间的,在
​ 这个期间计算机可以进行亿万次的循环。所以计算机的执行速度很快。

java语言中,实现线程有两种方式,那两种方式呢?

java支持多线程机制。并且java已经将多线程实现了,我们只需要继承就行了。

第一种方式:编写一个类,直接继承java.lang.Thread,重写run方法。
// 定义线程类
public class MyThread extends Thread{
public void run(){

	}
}
// 创建线程对象
MyThread t = new MyThread();
// 启动线程。
t.start();

第二种方式:编写一个类,实现java.lang.Runnable接口,实现run方法。
// 定义一个可运行的类
public class MyRunnable implements Runnable {
public void run(){

	}
}
// 创建线程对象
Thread t = new Thread(new MyRunnable());
// 启动线程
t.start();

注意:第二种方式实现接口比较常用,因为一个类实现了接口,它还可以去继承
其它的类,更灵活。

6、关于线程对象的生命周期?
新建状态
就绪状态
运行状态
阻塞状态
死亡状态

1、( 这部分内容属于了解)关于线程的调度

1.1、常见的线程调度模型有哪些?

​ 抢占式调度模型:
​ 那个线程的优先级比较高,抢到的CPU时间片的概率就高一些/多一些。
​ java采用的就是抢占式调度模型。

​ 均分式调度模型:
​ 平均分配CPU时间片。每个线程占有的CPU时间片时间长度一样。
​ 平均分配,一切平等。
​ 有一些编程语言,线程调度模型采用的是这种方式。

1.2、java中提供了哪些方法是和线程调度有关系的呢?

​ 实例方法:
​ void setPriority(int newPriority) 设置线程的优先级
​ int getPriority() 获取线程优先级
​ 最低优先级1
​ 默认优先级是5
​ 最高优先级10
​ 优先级比较高的获取CPU时间片可能会多一些。(但也不完全是,大概率是多的。)

​ 静态方法:
​ static void yield() 让位方法
​ 暂停当前正在执行的线程对象,并执行其他线程
​ yield()方法不是阻塞方法。让当前线程让位,让给其它线程使用。
​ yield()方法的执行会让当前线程从“运行状态”回到“就绪状态”。
​ 注意:在回到就绪之后,有可能还会再次抢到。

​ 实例方法:
​ void join()
​ 合并线程
​ class MyThread1 extends Thread {
​ public void doSome(){
​ MyThread2 t = new MyThread2();
​ t.join(); // 当前线程进入阻塞,t线程执行,直到t线程结束。当前线程才可以继续。
​ }
​ }

​ class MyThread2 extends Thread{

​ }

线程安全

2、关于多线程并发环境下,数据的安全问题。

2.1、为什么这个是重点?
以后在开发中,我们的项目都是运行在服务器当中,
而服务器已经将线程的定义,线程对象的创建,线程
的启动等,都已经实现完了。这些代码我们都不需要
编写。

​ 最重要的是:你要知道,你编写的程序需要放到一个
​ 多线程的环境下运行,你更需要关注的是这些数据
​ 在多线程并发的环境下是否是安全的。(重点:*****)

2.2、什么时候数据在多线程并发的环境下会存在安全问题呢?
三个条件:
条件1:多线程并发。
条件2:有共享数据。
条件3:共享数据有修改的行为。

​ 满足以上3个条件之后,就会存在线程安全问题。

2.3、怎么解决线程安全问题呢?
当多线程并发的环境下,有共享数据,并且这个数据还会被修改,此时就存在
线程安全问题,怎么解决这个问题?
线程排队执行。(不能并发)。
用排队执行解决线程安全问题。
这种机制被称为:线程同步机制。

​ 专业术语叫做:线程同步,实际上就是线程不能并发了,线程必须排队执行。

​ 怎么解决线程安全问题呀?
​ 使用“线程同步机制”。

​ 线程同步就是线程排队了,线程排队了就会牺牲一部分效率,没办法,数据安全
​ 第一位,只有数据安全了,我们才可以谈效率。数据不安全,没有效率的事儿。

2.4、线程同步,两个专业术语:

​ 异步编程模型:
​ 线程t1和线程t2,各自执行各自的,t1不管t2,t2不管t1,
​ 谁也不需要等谁,这种编程模型叫做:异步编程模型。
​ 其实就是:多线程并发(效率较高。)

​ 异步就是并发。

​ 同步编程模型:
​ 线程t1和线程t2,在线程t1执行的时候,必须等待t2线程执行
​ 结束,或者说在t2线程执行的时候,必须等待t1线程执行结束,
​ 两个线程之间发生了等待关系,这就是同步编程模型。
​ 效率较低。线程排队执行。

​ 同步就是排队。

合理的终止一个线程的运行(重要)


// 面试题:doOther方法执行的时候需要等待doSome方法的结束吗?
    //需要
public class Exam01 {
    public static void main(String[] args) throws InterruptedException {
        MyClass mc = new MyClass();

        Thread t1 = new MyThread(mc);
        Thread t2 = new MyThread(mc);

        t1.setName("t1");
        t2.setName("t2");

        t1.start();
        Thread.sleep(1000); //这个睡眠的作用是:为了保证t1线程先执行。
        t2.start();
    }
}

class MyThread extends Thread {
    private MyClass mc;
    public MyThread(MyClass mc){
        this.mc = mc;
    }
    public void run(){
        if(Thread.currentThread().getName().equals("t1")){
            mc.doSome();
        }
        if(Thread.currentThread().getName().equals("t2")){
            mc.doOther();
        }
    }
}

class MyClass {
    public synchronized void doSome(){
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }
    public synchronized void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}
// 面试题:doOther方法执行的时候需要等待doSome方法的结束吗?
    //需要
public class Exam01 {
    public static void main(String[] args) throws InterruptedException {
        MyClass mc = new MyClass();

        Thread t1 = new MyThread(mc);
        Thread t2 = new MyThread(mc);

        t1.setName("t1");
        t2.setName("t2");
        t1.start();
        Thread.sleep(1000); //这个睡眠的作用是:为了保证t1线程先执行。
        t2.start();
    }
}

class MyThread extends Thread {
    private MyClass mc;
    public MyThread(MyClass mc){
        this.mc = mc;
    }
    public void run(){
        if(Thread.currentThread().getName().equals("t1")){
            mc.doSome();
        }
        if(Thread.currentThread().getName().equals("t2")){
            mc.doOther();
        }
    }
}

class MyClass {
    public synchronized void doSome(){
        System.out.println("doSome begin");
        try {
            Thread.sleep(1000 * 10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("doSome over");
    }
    public synchronized void doOther(){
        System.out.println("doOther begin");
        System.out.println("doOther over");
    }
}
package com.bjpowernode.java.thread;
/*
守护线程
 */
public class ThreadTest14 {
    public static void main(String[] args) {
        Thread t = new BakDataThread();
        t.setName("备份数据的线程");

        // 启动线程之前,将线程设置为守护线程
        t.setDaemon(true);

        t.start();

        // 主线程:主线程是用户线程
        for(int i = 0; i < 10; i++){
            System.out.println(Thread.currentThread().getName() + "--->" + i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class BakDataThread extends Thread {
    public void run(){
        int i = 0;
        // 即使是死循环,但由于该线程是守护者,当用户线程结束,守护线程自动终止。
        while(true){
            System.out.println(Thread.currentThread().getName() + "--->" + (++i));
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

import java.util.ArrayList;
import java.util.List;

/*
1、使用wait方法和notify方法实现“生产者和消费者模式”

2、什么是“生产者和消费者模式”?
    生产线程负责生产,消费线程负责消费。
    生产线程和消费线程要达到均衡。
    这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方法。

3、wait和notify方法不是线程对象的方法,是普通java对象都有的方法。

4、wait方法和notify方法建立在线程同步的基础之上。因为多线程要同时操作一个仓库。有线程安全问题。

5、wait方法作用:o.wait()让正在o对象上活动的线程t进入等待状态,并且释放掉t线程之前占有的o对象的锁。

6、notify方法作用:o.notify()让正在o对象上等待的线程唤醒,只是通知,不会释放o对象上之前占有的锁。

7、模拟这样一个需求:
    仓库我们采用List集合。
    List集合中假设只能存储1个元素。
    1个元素就表示仓库满了。
    如果List集合中元素个数是0,就表示仓库空了。
    保证List集合中永远都是最多存储1个元素。

    必须做到这种效果:生产1个消费1个。
 */
public class ThreadTest16 {
    public static void main(String[] args) {
        // 创建1个仓库对象,共享的。
        List list = new ArrayList();
        // 创建两个线程对象
        // 生产者线程
        Thread t1 = new Thread(new Producer(list));
        // 消费者线程
        Thread t2 = new Thread(new Consumer(list));

        t1.setName("生产者线程");
        t2.setName("消费者线程");

        t1.start();
        t2.start();
    }
}

// 生产线程
class Producer implements Runnable {
    // 仓库
    private List list;

    public Producer(List list) {
        this.list = list;
    }
    @Override
    public void run() {
        // 一直生产(使用死循环来模拟一直生产)
        while(true){
            // 给仓库对象list加锁。
            synchronized (list){
                if(list.size() > 0){ // 大于0,说明仓库中已经有1个元素了。
                    try {
                        // 当前线程进入等待状态,并且释放Producer之前占有的list集合的锁。
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 程序能够执行到这里说明仓库是空的,可以生产
                Object obj = new Object();
                list.add(obj);
                System.out.println(Thread.currentThread().getName() + "--->" + obj);
                // 唤醒消费者进行消费
                list.notifyAll();
            }
        }
    }
}

// 消费线程
class Consumer implements Runnable {
    // 仓库
    private List list;

    public Consumer(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        // 一直消费
        while(true){
            synchronized (list) {
                if(list.size() == 0){
                    try {
                        // 仓库已经空了。
                        // 消费者线程等待,释放掉list集合的锁
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 程序能够执行到此处说明仓库中有数据,进行消费。
                Object obj = list.remove(0);
                System.out.println(Thread.currentThread().getName() + "--->" + obj);
                // 唤醒生产者生产。
                list.notifyAll();
            }
        }
    }
}


package com.bjpowernode.java.thread;

import java.util.ArrayList;
import java.util.List;

/*
1、使用wait方法和notify方法实现“生产者和消费者模式”

2、什么是“生产者和消费者模式”?
    生产线程负责生产,消费线程负责消费。
    生产线程和消费线程要达到均衡。
    这是一种特殊的业务需求,在这种特殊的情况下需要使用wait方法和notify方法。

3、wait和notify方法不是线程对象的方法,是普通java对象都有的方法。

4、wait方法和notify方法建立在线程同步的基础之上。因为多线程要同时操作一个仓库。有线程安全问题。

5、wait方法作用:o.wait()让正在o对象上活动的线程t进入等待状态,并且释放掉t线程之前占有的o对象的锁。

6、notify方法作用:o.notify()让正在o对象上等待的线程唤醒,只是通知,不会释放o对象上之前占有的锁。

7、模拟这样一个需求:
    仓库我们采用List集合。
    List集合中假设只能存储1个元素。
    1个元素就表示仓库满了。
    如果List集合中元素个数是0,就表示仓库空了。
    保证List集合中永远都是最多存储1个元素。

    必须做到这种效果:生产1个消费1个。
 */
public class ThreadTest16 {
    public static void main(String[] args) {
        // 创建1个仓库对象,共享的。
        List list = new ArrayList();
        // 创建两个线程对象
        // 生产者线程
        Thread t1 = new Thread(new Producer(list));
        // 消费者线程
        Thread t2 = new Thread(new Consumer(list));

        t1.setName("生产者线程");
        t2.setName("消费者线程");

        t1.start();
        t2.start();
    }
}

// 生产线程
class Producer implements Runnable {
    // 仓库
    private List list;

    public Producer(List list) {
        this.list = list;
    }
    @Override
    public void run() {
        // 一直生产(使用死循环来模拟一直生产)
        while(true){
            // 给仓库对象list加锁。
            synchronized (list){
                if(list.size() > 0){ // 大于0,说明仓库中已经有1个元素了。
                    try {
                        // 当前线程进入等待状态,并且释放Producer之前占有的list集合的锁。
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 程序能够执行到这里说明仓库是空的,可以生产
                Object obj = new Object();
                list.add(obj);
                System.out.println(Thread.currentThread().getName() + "--->" + obj);
                // 唤醒消费者进行消费
                list.notifyAll();
            }
        }
    }
}

// 消费线程
class Consumer implements Runnable {
    // 仓库
    private List list;

    public Consumer(List list) {
        this.list = list;
    }

    @Override
    public void run() {
        // 一直消费
        while(true){
            synchronized (list) {
                if(list.size() == 0){
                    try {
                        // 仓库已经空了。
                        // 消费者线程等待,释放掉list集合的锁
                        list.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // 程序能够执行到此处说明仓库中有数据,进行消费。
                Object obj = list.remove(0);
                System.out.println(Thread.currentThread().getName() + "--->" + obj);
                // 唤醒生产者生产。
                list.notifyAll();
            }
        }
    }
}

package com.bjpowernode.java.thread;

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

/*
使用定时器指定定时任务。
 */
public class TimerTest {
    public static void main(String[] args) throws Exception {

        // 创建定时器对象
        Timer timer = new Timer();
        //Timer timer = new Timer(true); //守护线程的方式

        // 指定定时任务
        //timer.schedule(定时任务, 第一次执行时间, 间隔多久执行一次);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date firstTime = sdf.parse("2020-03-14 09:34:30");
        //timer.schedule(new LogTimerTask() , firstTime, 1000 * 10);
        // 每年执行一次。
        //timer.schedule(new LogTimerTask() , firstTime, 1000 * 60 * 60 * 24 * 365);

        //匿名内部类方式
        timer.schedule(new TimerTask(){
            @Override
            public void run() {
                // code....
            }
        } , firstTime, 1000 * 10);

    }
}

// 编写一个定时任务类
// 假设这是一个记录日志的定时任务
class LogTimerTask extends TimerTask {

    @Override
    public void run() {
        // 编写你需要执行的任务就行了。
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String strTime = sdf.format(new Date());
        System.out.println(strTime + ":成功完成了一次数据备份!");
    }
}

标签:Thread,list,t1,线程,new,多线程,public
From: https://www.cnblogs.com/LDCnc-lili/p/17007105.html

相关文章

  • 多线程下载的神器
    如果你在linux下下载比较大的文件,网速也不太稳定是,用单线程下载就远远不如用多线程工具下载了。Manjaro中可以使用axel1、安装axelsudopacman-Saxel2、语法......
  • Java——多线程
    文章目录​​一.多线程概述​​​​1.什么是进程?什么是线程?​​​​2.进程和线程的关系​​​​3.多线程并发​​​​4.分析以下程序有几个线程​​​​5.Java实现线......
  • Redis 6.0 为什么要引入多线程呢?
    Reactor模式Redis是基于Reactor模式开发了网络事件处理器,这个处理器称为文件事件处理器。组成结构为4个部分:多套接字IO多路复用程序文件事件派发器事件处理器。在这里......
  • 多线程
    多线程什么是进程?什么是线程?进程是一个应用程序(1个进程是一个软件)。线程是一个进程中的执行场景/执行单元。一个进程可以启动多个线程。对于java程序来说,当在DOS命令......
  • GOQTTemplate3的多线程化改造
      GOQTTemplate3作为一个QT+OpenCV的平台,希望能够为使用者提供基础的跨平台的图像处理框架。图像处理算法和GUI两个线程的隔离,是必然需要的。在之前的版本中,都采用了......
  • 让人恶心的多线程代码,性能怎么优化!
    小姐姐味道(微信公众号ID:xjjdog)Java中最烦人的,就是多线程,一不小心,代码写的比单线程还慢,这就让人非常尴尬。通常情况下,我们会使用ThreadLocal实现线程封闭,比如避免SimpleD......
  • 多线程
    多线程什么是进程?什么是线程?进程是一个应用程序(1个进程是一个软件)。线程是一个进程中的执行场景/执行单元。一个进程可以启动多个线程。对于java程序来说,当在DOS命令......
  • 多线程必知必会的知识点
    说说阻塞队列的实现:可以参考ArrayBlockingQueue的底层实现(锁和同步都行);如果队列是空的,消费者会一直等待,当生产者添加元素时候,消费者是如何知道当前队列有元素的呢?如果让你来......
  • 多线程
    多线程什么是进程?什么是线程?进程是一个应用程序(1个进程是一个软件)。线程是一个进程中的执行场景/执行单元。一个进程可以启动多个线程。对于java程序来说,当在DOS命令......
  • 【并发技术系列】「多线程并发编程」技术体系和并发模型的基础探究(夯实基础)
    让我们通过本篇文章一同进入并发编程技术的世界里面,相信通过这篇文文章一定会对话你的并发技术体系有一定帮助以及夯实你的基础功底。基本概念并发concurrency并行paralleli......