A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

一、简介

1、什么是线程?

要解释线程,就必须明白什么是进程。

2、什么是进程呢?

    进程是指运行中的应用程序,每个进程都有自己独立的地址空间(内存空间),比如用户点击桌面的IE浏览器,就启动了一个进程,操作系统就会为该进程分配独立的地址空间。

要点:用户每启动一个进程,操作系统就会为该进程分配一个独立的内存空间。



二、线程--概念

在明白进程后,就比较容易理解线程的概念。

1、什么是线程呢?

    是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。同一进程中的多个线程之间可以并发执行。

写的太官方了,我的理解就是:

进程就类似一个班级,线程就类似这个班级里面的小学生,多个小学生可以同时做一件事,也可以排队等前面一个小学生做完(synchronized,同步锁操作);

可以让出当前的空间(yield),让另外一个小学生做,如果别人竞争不到或者没有人跟你竞争,那就自己继续干活;

也可以适当的休息一段时间(sleep),让另外一个小学生干活,之后再加入;

如果有两个要好的朋友,一个做完活了,要走了,你想让他等你,你可以用(join)让他等你做完活;

看过一个总结的特别简单的句子(进程就是打开一个APP,线程就是APP里面的各种操作)

2、线程的类型

线程中有两种类型,用户线程和守护线程

用户线程:运行在前台,执行具体的任务,程序的主线程,连接网络的子线程都是用户线程

守护线程:运行在后台,为其他前台线程服务

守护线程的特点:一旦所有的用户线程都结束运行,守护线程随着用户线程的结束而结束

(简单理解:用户线程就好比公司里面的CEO,经理这些,守护线程就好比保安,CEO,经理都下班了,保安也跟着下班呗)

设置线程成为守护线程(Daemon)

Thread thread = new Thread();
thread.setDaemon(true); // 调用对象的setDaemon方法,设置为守护线程,必须在start()前
thread.start();
三、Thread类和Runnable接口

如果想要在main方法中,定义多个线程去执行一段代码,java语言中实现了多线程编程的类和接口,在java.lang包中定义了Thread类和Runnable接口。

Thread类实现了Runnanble接口。Runnable对象为可运行对象,一个线程的执行是执行该对象里面的run方法

Thread的构造方法如下:

public Thread()
public Thread(Runnable target)
public Thread(String name)
public Thread(Runnable target, String name)
public Thread(ThreadGroup group, Runnable target)
public Thread(ThreadGroup group, String name)
public Thread(ThreadGroup group, Runnable target, String name)

/**
target:为线程的目标对象,即线程调用start()方法启动后运行那个对象的run()方法
name: 为该线程的名称
group:指定该线程属于哪一个组
*/
Thread的常用方法有:

public static Thread currentThread(); // 返回当前正在执行的线程对象
public void getName();// 返回当前的线程名称
public static void sleep(long millis); // 让当前线程休眠一段时间,让出当前CPU
public static void yield(); // 让出当前CPU,如果存在线程等待并且优先级别高的情况,则该线程获取到CPU执行空间;如果存在同等线程级别的情况,有可能当前让出的线程继续抢到CPU执行空间
public void run(); // 线程的线程体,方法都是写在这里面
public void start(); //JVM调用线程的run方法,启动线程开始
public void setDaemon(boolean on); // 设置线程为守护线程
四、线程的创建

创建线程有两种方法:

1、继承Thread并覆盖run方法

2、实现Runnable接口并重写run方法

继承Thread方法

简单做一个输出

public class ThreadA extends Thread {
    private String name;
    public ThreadA(String name){
        super(name);
        this.name = name;
    }
    @Override
    public void run(){
        System.out.println(Thread.currentThread().getName() + " 线程开始执行 ");
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程 "+name+"运行:"+i);
            try{
                sleep((int)Math.random()*10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.printf(Thread.currentThread().getName()+" 线程运行结束 ");
    }
}
main方法执行

public static void main(String [] args){
         System.out.println(Thread.currentThread().getName()+"主线程开始运行");
        ThreadA threadA = new ThreadA("A");
        ThreadA threadA1 = new ThreadA("B");
        threadA.start();
        threadA1.start();
        System.out.println(Thread.currentThread().getName()+"主线程结束执行");
}
/**
执行结果:
main主线程开始运行
main主线程结束执行
A 线程开始执行
子线程 A运行:0
B 线程开始执行
子线程 B运行:0
子线程 A运行:1
子线程 A运行:2
子线程 A运行:3
子线程 A运行:4
A 线程运行结束
子线程 B运行:1
子线程 B运行:2
子线程 B运行:3
子线程 B运行:4
B 线程运行结束  
*/
这里可以看出,两个线程互相抢占资源,输出的信息,都是乱的;再看main主线程,一开始就结束了运行,可以看出我们的子线程是执行成功的;

如果我们想要主线程等待子线程执行成功之后,再做下一步操作呢?可以用Join() 方法

public static void main(String[] args){
        System.out.println(Thread.currentThread().getName()+"主线程开始运行");
        ThreadA threadA = new ThreadA("A");
        ThreadA threadA1 = new ThreadA("B");
        threadA.start();
        threadA1.start();
        // join的意思:等待指定线程终止,这里可以理解为: main主线程等待子线程的终止
        threadA.join();
        threadA1.join();
        System.out.println(Thread.currentThread().getName()+"主线程结束执行");
}
/**
main主线程开始运行
A 线程开始执行
子线程 A运行:0
B 线程开始执行
子线程 B运行:0
子线程 A运行:1
子线程 A运行:2
子线程 A运行:3
子线程 A运行:4
A 线程运行结束
子线程 B运行:1
子线程 B运行:2
子线程 B运行:3
子线程 B运行:4
B 线程运行结束
main主线程结束执行
*/
仔细看输出日志,main主线程结束执行排在最后面;

看的这个排序是在不爽,我们想要让A输出完,B再输出的话,怎么处理呢?

用synchronized 同步锁,他的意思是:被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

  @Override
    public synchronized void run(){
        System.out.println(Thread.currentThread().getName() + " 线程开始执行 ");
        for (int i = 0; i < 5; i++) {
            System.out.println("子线程 "+name+"运行:"+i);
            try{
                sleep((int)Math.random()*10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName()+" 线程运行结束 ");
    }

/**
执行结果:
main主线程开始运行
B 线程开始执行
子线程 B运行:0
子线程 B运行:1
子线程 B运行:2
子线程 B运行:3
子线程 B运行:4
B 线程运行结束
A 线程开始执行
子线程 A运行:0
子线程 A运行:1
子线程 A运行:2
子线程 A运行:3
子线程 A运行:4
A 线程运行结束
main主线程结束执行
*/
嗯,很奈斯

使用Synchronized 表示多个线程同时调用一个方法的时候,没有调用wait()或者sleep()方法的话,只能先进入的一个线程执行完毕之后,后一个线程才能进入。

实现Runnable接口

实现两个线程执行同一个方法,打印输出10、9、8...

// 多个线程,简单输出10、9、8...
public class RunnableA implements Runnable {
    private int count = 10;
    @Override
    public void run() {
        while (count > 0){
            System.out.println("当前线程名称是 >>> "+Thread.currentThread().getName()+" ,count为 >>> "+count);
            count--;
        }
    }
}
调用方法

public static void main(String[] args){
        RunnableA ra = new RunnableA();
        Thread tA = new Thread(ra,"A");
        Thread tB = new Thread(ra,"B");
        tA.start();
        tB.start();
}
/**
执行结果:
当前线程名称是 >>> A ,count为 >>> 10
当前线程名称是 >>> B ,count为 >>> 10
当前线程名称是 >>> B ,count为 >>> 8
当前线程名称是 >>> A ,count为 >>> 9
当前线程名称是 >>> B ,count为 >>> 7
当前线程名称是 >>> A ,count为 >>> 6
当前线程名称是 >>> B ,count为 >>> 5
当前线程名称是 >>> A ,count为 >>> 4
当前线程名称是 >>> A ,count为 >>> 2
当前线程名称是 >>> A ,count为 >>> 1
当前线程名称是 >>> B ,count为 >>> 3
*/
可以看出,两个线程同时执行一个方法,得出的结果存在两个10的情况,我们可以加synchornized 同步锁去处理,但是这样,出现的情况就是要么 tA全执行完,要么tB全执行完,两个线程变成了单个线程去使用。

如果我们想要 tA线程输出双数,tB线程输出单数的情况,应该怎么处理呢?

嘿,上代码

// 多个线程,简单输出10、9、8...
public class RunnableA implements Runnable {
    private int count = 10;

    private int i = 0;
    @Override
    public void run() {

        // synchronized (this) 表示的是该代码块同步,this表示的是当前进来的线程。
        synchronized (this) {
            while (count > 0) {
                notify();   // 唤醒当前进来的线程,如果没有在等待状态,也可以唤醒
                System.out.println("当前线程名称是 >>> " + Thread.currentThread().getName() + " ,count为 >>> " + count);
                count--;
                try {
                    //  wait:使当前线程进入等待状态,并且释放当前对象所持有的锁
                    // (简单理解就是 synchronized 锁住的对象,有方法,有变量,简单理解为当前线程释放了对资源的控制)
                    wait();
                    // System.out.println(Thread.currentThread().getName() + ">>>" + Thread.currentThread().isAlive()+" >>> "+(i++));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}


调用方法

public static void main(String[] args){
        RunnableA ra = new RunnableA();
        Thread tA = new Thread(ra,"A");
        Thread tB = new Thread(ra,"B");
        tA.start();
        tB.start();
}

/**
执行结果:
当前线程名称是 >>> A ,count为 >>> 10
当前线程名称是 >>> B ,count为 >>> 9
当前线程名称是 >>> A ,count为 >>> 8
当前线程名称是 >>> B ,count为 >>> 7
当前线程名称是 >>> A ,count为 >>> 6
当前线程名称是 >>> B ,count为 >>> 5
当前线程名称是 >>> A ,count为 >>> 4
当前线程名称是 >>> B ,count为 >>> 3
当前线程名称是 >>> A ,count为 >>> 2
当前线程名称是 >>> B ,count为 >>> 1
*/
【注:博主一直看不懂 wait()  后面说的,并且释放该线程所持有的锁,后面捋了一遍,我的简单粗暴的理解就是:好比有一个屋子,里面很多吃的,一群饿鬼在门外面抢着要进去吃东西,你抢先进去了,然后马上把门关上了,等你吃的差不多了,就要把门打开了。不能这么自私吧。栗子不是很恰当,反正意思就是说,一个线程使用wait()进入等待状态的时候,同时也会释放掉对当前锁的控制,让其他线程可以抢占到锁】
---------------------
转载,仅作分享,侵删
作者:小-虾米
原文:https://blog.csdn.net/qq_35188937/article/details/85290965


1 个回复

倒序浏览
奈斯奈斯
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马