Java多线程学习总结

进程与线程

进程:内存中运行的应用程序,每个进程拥有独立的运行空间
线程:进程的一个执行路径,共享一个内存空间,线程之间可以任意切换,并发执行,又叫轻量级进程

线程调度

1.分时调度
所有线程轮流使用cpu,平均分配cpu调度时间
2.抢占式调度
线程优先级高的先使用cpu,线程优先级相同时会随机选择。Java使用的是抢占式调度
在一核(1个cpu)情况下其实多线程并不能提高程序的运行速度,但可以提高程序运行效率
,让cpu使用率更高

同步、异步 、并发、并行

同步:排队执行,效率低,但安全
异步:同时执行,效率高,但数据不安全
并发:多个事件在同一个时间段内发生
并行:多个事件在同一时刻发生

线程

1.继承Thread类,重写run()方法

public class MyThread extends Thread {

    public void run(){
    
    }
}
//启动线程
Mythread th = new MyThread();
th.start();

2.实现 Runnable 接口

public class MyThread extends Thread {

    public void run(){
    
    }
}
//启动线程
  //1.创建任务
  Mythread th = new MyThread();
  //2.创建一个线程并为其分配任务
  Thread thread = new Thread(th);
  //3.执行这个线程
  thread.start();

实现Runnable接口实现线程的优势:

  • 通过创建任务,然后给线程分配的方式,更适合多个线程执行相同的任务
  • 避免单继承的局限性
  • 任务与线程本身是分离的,提高程序健壮性
  • 后续学习的线程池技术,接收Runnable类型的任务,不接收Thread类型的线程
线程休眠与阻塞
  • 休眠:在run()方法中调用sleep(休眠毫秒数)方法
  • 阻塞:休眠会导致阻塞,并且在线程执行时,消耗大量时间的操作,也会导致线
    程进入阻塞状态(如等待用户输入)
用户线程和守护线程
  • 用户线程:当一个进程不包含任何存活的用户线程时,进程结束。直接创建的线程
    都是用户线程
  • 守护线程:当最后一个用户线程结束时,所有守护线程自动结束。
  • 守护线程设置:在守护线程启动前调用setDaemon(true)方法.
线程不安全的解决

1.同步代码块:

//  synchronized(锁对象){//此时锁对象可以指定,但想要实现同步
        //所有线程必须看同一把锁
//  } 

2.同步方法:

//  权限修饰符 synchronized 返回类型 方法名(){
//  类是非静态,此时锁对象是this,如果类中有多个同步方法,则它们共用this锁
//  一个方法被上锁时(正在执行),其它方法不能被执行
//  当类是静态时,锁对象为: 类名.class
//  
//  } 

3.显式锁(Lock类)Lock 子类 ReentrantLock

    /*
    private Lock lok = new ReentrantLock();
    public void run(){
        lok.lock();
        //要上锁的代码块
        lok.unlock();
    
    }
    */
公平锁和非公平锁

以上三种上锁方式都是非公平的,如果想要实现公平锁,可以在创建显式锁时传
入一个boolean参数:true,其默认为false(非公平)

线程死锁
  • 如何解决:当一个对象方法会产生锁时,尽量不要去调用另一个对象会产生锁的方法
多线程通信问题(生产者和消费者问题)
线程的六种状态

1.NEW状态:被创建但未运行
2.Runnable状态:运行状态
3.Blocked状态:阻塞状态
4.Waiting状态:无限等待状态,未设置等待时间
5.timedWaiting状态:计时等待状态,设置等待时间,等待时间过后自动唤醒
6.Terminate状态:死亡状态

带返回值的线程callable
class MyCallable implements Callable<T> {

        @Override
        public T call() throws Exception {
            return Object类型对象;
        }
}
    //启动线程
    MyCallable my = new MyCallable();
    FutureTask task = new FutureTask(my);
    //task.isDone();//判断线程是否执行完,会返回一个boolean类型的值
    new Thread(task).start();
线程池 ExecutorService
  • 线程池就是可以容纳多个线程的容器,线程池中的线程可以反复使用省去了频繁
    创建线程的操作,节省了大量时间和资源
  • 线程池好处:降低资源消耗,提高响应速度,提高线程的可管理性

1.缓存线程池 (长度无限制)
任务接收后的执行流程:判断线程池是否存在空闲,存在则使用,不存在则创建线
程并放入线程池,然后使用

  • 缓存线程池使用:
//ExecutorService service = Executors.newCachedThreadPool();
//service.execute(传入一个任务对象)
// 无法接收一个继承了Thread的线程对象 

2.定长线程池
任务接收后的执行流程:判断线程池中是否有空闲线程,存在则使用;不存在且
线程池未满,则创建一个新线程并放入线程池并使用;不存在且线程池满,则排队
等待线程池中的线程空闲

  • 定长线程池使用:
//ExecutorService service = Executors.newFixedThreadPool(
// 线程池大小);
//service.execute(传入一个任务对象)
// 无法接收一个继承了Thread的线程对象 

3.单线程线程池
任务接收后的执行流程:判断线程池中是否有空闲线程,存在则使用;不存在则
等待线程池中的线程空闲

  • 单线程线程池使用:
//ExecutorService service = Executors.newSingleThreadExecutors();
//service.execute(传入一个任务对象)
// 无法接收一个继承了Thread的线程对象 

4.周期定长线程池
任务接收后的执行流程:判断线程池中是否有空闲线程,存在则使用;不存在则
等待线程池中的线程空闲

  • 周期定长线程池使用:
class Demo{
public static void main(String[] args) 
throws ExecutionException, InterruptedException {
        ScheduledExecutorService service = 
        Executors.newScheduledThreadPool(2);//指定线程池长度
        /**
         * 1.定时执行一次
         * 参数1:一个任务对象
         * 参数2:时长数值
         * 参数3:时长的单位,使用TimeUnit的常量指定
         */
        service.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("lcl");
            }
        },5,TimeUnit.SECONDS);

        /**
         * 2。周期性执行任务
         * 参数1:一个任务对象
         * 参数2:延迟时长数值(第一次执行在多长时间以后)
         * 参数3:周期时长数值(第一次执行完,间隔多久执行一次)
         * 参数4:时长单位,使用TimeUnit的常量指定
         */
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println("床前明月光");
            }
        },3,3,TimeUnit.SECONDS);
    }
}
Lambda表达式(JDK8引入)
public class LambdaDemo {

    public static void main(String[] args) {
        print(new MyMath() {
            @Override
            public int sum(int x, int y) {
                return x + y;
            }
        }, 100, 200);

        /**
         * Lambda表达式:函数式编程思想
         * 当接口只有一个抽象方法时才能使用Lambda表达式
         */
        print((int x, int y) ->
        {
            return x + y;
        }, 100, 200);
    }

    public static void print(MyMath math, int x, int y) {
        System.out.println(math.sum(x, y));
    }

    static interface MyMath {
        int sum(int x, int y);
    }
}
  • 当接口只有一个抽象方法时才能使用Lambda表达式
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值