2021-02-28

多线程

一、线程与进程

进程

  • 是指一个内存中运行的应用和程序,每个进程都有一个独立的内存空间

线程

  • 是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程
  • 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程

二、多线程的优点

  1. 提高应用程序的响应
  2. 提高计算机CPU的利用率
  3. 改善程序结构,将将既长又复杂的进程分为多个线程,独立运行,利于理解和修改

三、线程的调度

分时调度

  • 分时调度是指所有的线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。

抢占式调度

  • 优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
  • CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对于我们的感觉要快,看上去就是在同一时刻运行,其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。

四、同步与异步

  • 同步 :排队执行,效率低但是安全
  • 异步 :同时执行。效率高但是数据不安全

五、并发与并行

  • 并发 :指两个或多个事件在同一个时间段内发生。
  • 并行 :指两个或多个事件在同一时刻发生(同时发生)。

六、多线程的实现方式

1.Thread的实现

先创建线程类MyThread,注意要继承并且重写run方法

public class MyThread extends Thread {
    /**
     * run方法就是线程要执行的任务方法
     */
    @Override
    public void run() {
        //这里的代码就是一条新的执行路径,里面要执行的任务
        //这个执行路径的触发方式,不是调用run方法,而是通过thread对象的start()方法来启动任务
        for(int i = 0; i < 10; i++){
            System.out.println("锄禾日当午"+i);
        }
    }
}

在主线程中创建MyThread类的对象然后调用start方法

public class Txt {
    public static void main(String[] args) throws InterruptedException {
        //抢占式分配  谁先走完是随机的
        MyThread m = new MyThread();
        m.start();
        for(int i = 0; i < 10; i++){
            System.out.println("汗滴禾下土"+i);
        }
    }
}

Thread常见应用场景

可以结合匿名内部类使用,使得写法比较简洁

new Thread(){
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("疑是地上霜"+i);
                }
            }
        }.start();

也可以在里面放入任务

new Thread(new Runnable() {
            @Override
            public void run() {
                
            }
        }).start();

2.Runnable任务实现
创建MyRunnable类,实现Runnable接口,同时也要重写run方法

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        //线程的任务
        for (int i = 0; i < 10; i++){
            System.out.println("床前明月光"+i);
        }
    }
}        

创建MyRunnable类的对象,将其作为Thread构造器的参数,创建一个新的Thread对象t,调用t的start方法

//      实现Runnable
        //1.    创建一个任务对象
        MyRunnable r = new MyRunnable();
        //2.    创建一个线程,并为其分配一个任务
        Thread t = new Thread(r);
        //3.    执行这个线程
        t.start();

实现Runnable 接口 与 继承Thread父类相比有如下优势:

  1. 通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同的任务的情况。
  2. 可以避免(单)继承所带来的局限性,java中只有单继承,但是允许多实现并且可以去实现其他的类,还能再去继承一个
  3. 通过创建任务,然后给线程分配的方式任务与线程本身是分离的,提高了程序的健壮性
  4. 更重要的点:
    线程池技术,接受Runnable类型的任务,而不接收Thread类型的线程

Callable使用

public class callable {
    /**
     * 第三种线程结构
     */
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        
        //判断线程是否完毕
        futureTask.isDone();
        
        //取消子线程,不想等待 得传入参数 传入true就是取消
        futureTask.cancel(true);
        new Thread(futureTask).start();
        Integer j = futureTask.get();
        System.out.println("返回值为"+j);

        for (int i = 0; i < 10; i++){
            Thread.sleep(1000);
            System.out.println(i);
        }

    }
    static class MyCallable implements Callable{

        @Override
        public Integer call() throws Exception {

            for (int i = 0; i < 10; i++){
                Thread.sleep(1000);
                System.out.println(i);
            }
            return 100;
        }
    }
}

Runnable 与 Callable

接口定义 :

  1. //Callable接口
public interface Callable<V> { 
	V call() throws Exception; 
}
  1. //Runnable接口
public interface Runnable { 
	public abstract void run(); 
}

Runnable 与 Callable的相同点

  • 都是接口
  • 都可以编写多线程程序
  • 都采用Thread.start()启动线程

Runnable 与 Callable的不同点

  • Runnable没有返回值;Callable可以返回执行结果

  • Callable接口的call()允许抛出异常;Runnable的run()不能抛出

Callable获取返回值

  • Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

七、线程方法

  1. 获取线程的名称

得到当前线程的名称

Thread.currentThread().getName();
  1. 线程休眠

静态方法可以直接调用,使线程休眠,可以输入毫秒或者毫秒加纳秒

Thread.sleep(millis:1000);
  1. 线程阻塞

线程阻塞不仅仅包括休眠,可以理解为所有消耗时间的操作,例如中间读取文件的时间

  1. 线程中断

一个线程是一个独立的执行路径,它是否应该结束,应该由其自身决定

在早期的jdk版本中,Thread有stop方法,但直接stop无法对其占用的资源进行合理的释放。
线程是否关断应当由线程自身来决定,通过中断标记的形式,在某些情况下查看标记,抛出异常。
在调用Interrupt方法时,如果出现下述情况进入catch块中的InterruptException异常

实列:

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0 ;i<10;i++){
            System.out.println("锄禾日当午"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//主函数
public class MyInterrupted {
    public static void main(String[] args) {
        MyRunnable tr = new MyRunnable();
        Thread t = new Thread(tr);
        t.start();
        t.interrupt();
    }
}

线程:分为守护线程和用户线程

  • 用户线程:

当一个进程不包含任何的存活的用户线程时,进行结束直接创建的线程就是用户线程

  • 守护线程:

守护用户线程,当最后一个用户线程结束时,所有用户线程自动死亡

  • 设置守护线程:

在启动前设置

可以通过setDaemon(true)将子线程设置为守护线程

八、线程安全

方法一:同步代码块

​ synchronized(同步监视器){

​ //需要被同步的代码

​ }

说明:

1.操作共享数据的代码,即为需要被同步的代码

​2.共享数据:多个线程共同操作的变量

​3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。

​ 要求:多个线程必须要共用同一把锁

​ 补充:在实现runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。

​ 在继承thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。

方法二:同步方法

如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。

Runnable接口:

​ synchronized void 方法名(){ //同步监视器:this

​ //需要被同步的代码

​ }

Thread继承:

​ static synchronized void 方法名(){ //同步监视器: object.class

​ //需要被同步的代码

​ }

  1. 同步方法本身仍然涉及到同步监视器,只是不需要我们显示的声明

  2. 非静态的同步方法,同步监视器是:this
    静态的同步方法,同步监视器是:当前类本身

九、线程的六种状态

  1. 尚未启动 new
  2. 执行中 Runnable
  3. 排队时 Blocked
  4. 无期限等待 waiting
  5. 等待有休眠时间 waiting
  6. 终止 trminated

十、线程池 Executors

线程池的好处

  • 降低资源消耗。
  • 提高响应速度。
  • 提高线程的可管理性。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值