多线程笔记

创建线程的方式

  1. 继承Thread类,重写run方法
public class MyThread extends Thread{
    int i=0;
    @Override
    public void run() {
        for (int j = 0; j < 100; j++) {
            i++;
            System.out.println(getName()+" "+i);
        }
    }
}
  1. 实现Runnable接口,重写run方法,再创建实例,以该类的实例为Thread的参数来创建线程
public class MyRunnable implements Runnable{
    int i=0;
    @Override
    public void run() {
        for (int j = 0; j < 100; j++) {
            i++;
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i==20){
                MyRunnable myRunnable = new MyRunnable();
                new Thread(myRunnable,"线程1").start();
                new Thread(myRunnable,"线程2").start();
            }
        }
    }
}
  1. 实现Callable接口,重写call方法,再创建该对象的实例,以该对象的实例为FutureTask的参数来创建FutureTask对象,再以FutureTask对象作为Thread的参数创建并启动线程。通过FutureTask对象.get()获取线程的返回值
public class MyCallable implements Callable<Integer> {
    int i=0;
    @Override
    public Integer call() throws Exception {
        for (int j = 0; j < 100; j++) {
            i++;
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
        return i;
    }

    public static void main(String[] args) {
        MyCallable myCallable = new MyCallable();
        FutureTask<Integer> integerFutureTask = new FutureTask<>(myCallable);
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i==20){
                new Thread(integerFutureTask,"有返回值的线程1").start();
            }
        }
        try {
            System.out.println("返回值:"+integerFutureTask.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}
  1. 通过线程池创建线程
public class Main {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //Runnable接口的实现类
        MyRunnable myRunnable = new MyRunnable();
        for (int i = 0; i < 10; i++) {
            executorService.execute(myRunnable);
        }
        //关闭线程池
        executorService.shutdown();
    }
}

不同线程创建方法的优劣

使用Runnable和Callable实现多线程和基础Thread类实现多线程的区别
  1. 实现接口实现多线程的方式还可以再继承其他的类;
  2. 实现接口的方式多个线程可以共享同一个target对象,非常适合多个线程处理同一个资源的情况;
  3. 实现接口实现多线程的方程获取当前线程时,比继承Thread类稍微复杂一些,需要使用Thread.currentThread()获取当前线程,而继承Thread类的只需要使用this即可。

线程同步的方式

  1. 使用同步代码块的方式(给代码添加synchronized关键字)
public class MyRunnable implements Runnable{
    int i=0;
    @Override
    public void run() {
        for (int j = 0; j < 100; j++) {
            try {
                Thread.currentThread().sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //关键字
            synchronized (this){
                i++;
            }

            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i==20){
                MyRunnable myRunnable = new MyRunnable();
                new Thread(myRunnable,"线程1").start();
                new Thread(myRunnable,"线程2").start();
            }
        }
    }
}

  1. 同步方法方式
    将run方法需要执行的代码提取到一个方法中,在方法前添加synchronized关键字。
    其中方法分为静态方法和普通方法
    静态方法锁住的是整个类,而普通方法只能锁住当前的对象
public class MyThread extends Thread{
   static int i=0;
    @Override
    public void run() {
        method();
    }

    //通过同步方法的方式实现线程安全
    public static synchronized void method(){
        for (int j = 0; j < 100; j++) {
            try {
                sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i==50){
                new MyThread().start();
                new MyThread().start();
            }
        }
    }
}
  1. Lock方式实现线程同步
    对操作不安全的代码进行加锁,操作完成后进行解锁,从而实现线程同步
public class MyCallable implements Callable<Integer> {
    static int i=0;
    //创建锁对象
    Lock l=new ReentrantLock();
    @Override
    public Integer call() throws Exception {
        for (int j = 0; j < 100; j++) {
            try {
                Thread.currentThread().sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            l.lock();//加锁
            i++;
            l.unlock();//解锁
            System.out.println(Thread.currentThread().getName()+" "+i);
        }
        return i;
    }

    public static void main(String[] args) {
        MyCallable myCallable = new MyCallable();

        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            if (i==20){
                FutureTask<Integer> integerFutureTask1 = new FutureTask<>(myCallable);
                new Thread(integerFutureTask1,"有返回值的线程1").start();
                FutureTask<Integer> integerFutureTask2 = new FutureTask<>(myCallable);
                new Thread(integerFutureTask2,"有返回值的线程2").start();
            }
        }
    }
}

进程与线程的区别

  1. 线程是“进程代码段”的一次执行流程。一个进程由一个或者多个线程组成,一个进程至少拥有一个线程;
  2. 线程是CPU调度的最小单位,进程是操作系统分配资源的最小单位,线程的尺度划分小于进程,使得多线程程序的并发性高;
  3. 线程是出于高并发的调度诉求从进程内部演进而来。线程的出现既充分发挥了CPU的计算性能,又弥补了进程调度过于笨重的问题;
  4. 进程之间是相互独立的,但是进程内部的各个线程之间并不完全独立。各个线程之间共享进程的方法区内存、堆内存、系统资源。
  5. 切换速度不同:线程上下文切换比进程上下文切换要快。所以有时候线程也称为轻量级进程。

守护线程

  1. 守护线程必须在启动前将其守护状态设置为true,启动之后不能再将用户线程设置为守护线程,否则会抛出异常;具体操作就是线程在start之前,调用线程实例的setDaemon(true),设置其daemon属性为true;
  2. 守护线程存在被JVM强行终止的风险,所以在守护线程中尽量不要去访问系统资源,JVM运行结束时守护线程自动终止;
  3. 守护线程创建的线程也是守护线程,所以想要在守护线程中创建用户线程,想要在新线程启动前,调用setDaemon(false)方法,将新线程是守护状态改为false。

为什么使用线程池?

  1. 降低资源消耗,提高线程的利用率,降低线程创建和销毁线程的消耗;
  2. 降低响应时间,任务来了,直接就有线程可以执行,而不是先创建再执行;
  3. 提高线程的可管理性,线程是稀缺资源,使用线程池可以统一分配调优监控。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CSDN_SmallAnnum

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值