多线程(初阶)

多线程(初阶),第1张

目录

1. 线程

1.1 线程的提出

1.2 线程的概念

1.3 线程和进程的关系

1.4 Java 的线程和 *** 作系统线程

 2. Java中创建线程

2.1 继承Thread类 重写run()方法

2.2 实现Runnable接口 重写run()方法

 2.3 继承Thread类 重写run()方法,使用匿名内部类

2.4 实现Runnable接口 重写run()方法,使用匿名内部类 

2.5 start和run的区别 

3. Thread类及常用方法 

 3.1 Thread的常见构造方法​

3.2 Thread类的几个常见属性

 3.3 等待一个线程

3.4 休眠当前线程

3.5 获取当前线程的引用

4. 线程的状态


1. 线程 1.1 线程的提出

在进程的学习中,我们了解到引入进程就是为了“并发编程”,虽然进程能解决并发的问题,但是我们认为还不是不够理想。

因为创建进程/销毁进程/调度进程,开销有点大:

  • 创建进程需要分配资源;
  • 销毁进程需要释放资源;

频繁的创建和销毁进程就会开销较大。

于是就有了“线程”(Thread)的 概念,线程在有些系统上也叫做"轻量级进程"。

1.2 线程的概念

一个线程就是一个 “执行流”.,每个线程之间都可以按照顺讯执行自己的代码.,多个线程之间 “同时” 执行着多份代码

  • 创建线程比创建进程更高效
  • 销毁线程比销毁进程更高效
  • 调度线程比调度进程更高效
1.3 线程和进程的关系
  1. 进程和线程是包含关系。每个进程至少有一个线程存在,即主线程。
  2. 进程和进程之间不共享内存空间.。同一个进程的线程之间共享同一个内存空间。每个进程拥有独立的内存空间(虚拟地址空间),同一进程多个线程共用这个内存空间(虚拟地址空间)
  3. 进程是系统分配资源的最小单位,线程是系统调度的最小单位。 
1.4 Java 的线程和 *** 作系统线程

在Java中使用 Thread 类这个类的对象来表示 *** 作系统中的线程

  • 在 *** 作系统中 , PCB是用来描述线程的.
  • 在Java代码中 , Thread类 是用来描述线程的
 2. Java中创建线程 2.1 继承Thread类 重写run()方法

(1)创建Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。

(2)创建了Thread子类的实例,即创建了线程对象。

(3)调用线程对象的start()方法来启动该线程。

代码示例:

class MyThread extends Thread{
    @Override
    public void run() {
    	System.out.println("线程名:" + currentThread().getName());
    }
}

public class ThreadDemo {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start();
    }
}

 

2.2 实现Runnable接口 重写run()方法

(1)定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。

(2)创建 Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。

(3)调用线程对象的start()方法来启动该线程。

线程的执行流程很简单,当执行代码start()时,就会执行对象中重写的void run()方法,该方法执行完成后,线程就消亡了。

代码示例:

public class demo2 {

    public static void main(String[] args) {

        MyThread02 target=new MyThread02();

        Thread t1=new Thread(target);
        Thread t2=new Thread(target);

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

class MyThread02 implements Runnable{

    @Override
    public void run() {
        System.out.println("线程名:"+Thread.currentThread().getName());
    }
}

 

 2.3 继承Thread类 重写run()方法,使用匿名内部类

 代码示例:

public class demo3 {
        public static void main(String[] args) {
            Thread t = new Thread(){
                @Override
                public void run() {
                    System.out.println("hello thread !");
                }
            };
            t.start();
        }
}

2.4 实现Runnable接口 重写run()方法,使用匿名内部类 

代码示例: 

public class demo4 {
    public static void main(String[] args) {
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello thread !!!");
            }
        });
        t.start();
    }
}

2.5 start和run的区别 

 代码示例:

class MyThread extends Thread{
    @Override
    public void run() {
        System.out.println(" thread !!! ");
    }
}
public class demo5 {
    public static void main(String[] args) {
        Thread t = new MyThread();
        t.start(); //1
        //t.run(); // 2
    }
}

 上面代码中start和run的区别,程序的执行上结果是一样的,但实际上:

  • 使用 t.start() 会创建一个新的 PCB ,新的 PCB 链接在链表上,然后执行 myThread.run() 方法
  • 使用 t.run() 会直接调用 myThread.run()
3. Thread类及常用方法   3.1 Thread的常见构造方法

代码示例:

        Thread t1 = new Thread();
        Thread t2 = new Thread(new MyRunnable());
        Thread t3 = new Thread("线程命名");
        Thread t4 = new Thread(new MyRunnable(), "线程命名");
3.2 Thread类的几个常见属性
属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()
  •  ID是唯一的标识,不同的线程不会重复
  • 名称就是线程的名字
  • 状态就是线程的状态,存在的意义就是辅助进行线程调度
  • 优先级高的更容易被调度
  • 创建的线程,默认不是后台线程,注意:JVM会在一个进程的所有非后台线程结束后,才会结束
  • 是否存活,就是内核中的PCB是不是销毁了,也就是说系统中的线程是不是销毁了.简单理解,为run方法是否运行结束了
 3.3 等待一个线程

线程和线程之间,调度顺序是完全不确定的…如果想要线程的顺序可控,线程等待就是一种方法。

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等 millis 毫秒
public void join(long millis, int nanos)等待线程结束,时间单位更精确

 当执行到 t.join() 时,这里的线程会阻塞等待。

假定一种情况,A吃饭时没带钱包,就请B帮他把钱包送过来:

代码示例:

public class Main {
    private static class B extends Thread {
        @Override
        public void run() {
            // 模拟 B 要做很久的工作
            try {
                TimeUnit.SECONDS.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            println("B 说:我把钱包送来了");
        }
    }

    private static void println(String msg) {
        Date date = new Date();
        DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(format.format(date) + ": " + msg);
    }

    public static void main(String[] args) throws InterruptedException {
        B b = new B();
        b.start();
        println("A 自己先去吃饭");
        // 有 join 和没有 join 的区别
        b.join();
        println("A 说:B 给我把钱送来了,结账走人");
    }
}

 不加b.join时,可以看到A的 *** 作时顺序执行的,并没有等待B的 *** 作完成。

加了b.join时,就可以保证A一定是等到B *** 作完成后才继续执行自己的 *** 作,达到了顺序可控。

3.4 休眠当前线程
public static void sleep(long millis)   // 休眠当前线程millis毫秒

从线程状态的角度来看,调用sleep(xx),就是让当前线程从“运行态”-->“阻塞态”。此处的阻塞是指线程必须要等待某个条件:即要求时间过去xx之后。当这段时间过去后(条件满足),线程才会从“阻塞态”-->“就绪态”,此时如果线程被调度器选中,就会接着之前的指令开始执行,表现为sleep之后的语句执行。

整体的外部表现为让线程休眠一段时间。

另外一种让线程休眠的方法为:

TimeUnit.SECONDS.sleep();
3.5 获取当前线程的引用
方法说明
public static Thread currentThread();返回当前线程对象的引用
Thread.currentThread();

该方法会返回当前线程对象的引用,在哪个线程中调用的该方法,就返回哪个对象。

public class Main {
    static class MyThread extends Thread {
        @Override
        public void run() {
            printCurrentThreadAttributes();
        }
    }
    private static void printCurrentThreadAttributes() {
        // 返回当前(这个方法是在哪个线程中被调用的)线程的引用
        Thread t = Thread.currentThread();
        System.out.println(t.getId());
        System.out.println(t.getName());
    }
    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        t1.setName("t1");
        t1.start();

        MyThread t2 = new MyThread();
        t2.setName("t2");
        t2.start();

        MyThread t3 = new MyThread();
        t3.setName("t3");
        t3.start();

        printCurrentThreadAttributes();
    }
}

 所以固定的方法被不同的线程调用,返回的就是不同的对象。

4. 线程的状态
  •  NEW: Thread 对象创建了,但是内核没有创建出PCB。
  • RUNNABLE: 当前的PCB创建出来了,这个PCB就绪了.这个线程可能在CPU上运行,也可能在就绪队列中排队。
  • BLOCKED: 线程中尝试进行加锁,结果发现锁已经被其他线程占用了,此时PCB也会处于阻塞状态.这个等待会在其他线程释放锁之后被唤醒。
  • WAITING: PCB处于阻塞状态(死等)
  • TIMED_WAITING: 表示PCB在阻塞队列中等待,这个等待是有结束时间的等待。
  • TERMINATED: 表示当前PCB已经结束了,但是Thread对象还在,此时调用获取状态,得到的就是这个状态。
     

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/905890.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-15
下一篇 2022-05-15

发表评论

登录后才能评论

评论列表(0条)

保存