java 多线程

目录

1.线程的相关概念

(1)进程、线程

(2)并行、并发

(3)线程的生命周期

2.多线程的实现方式

(1)继承Thread类

(2)实现Runnable接口

(3)实现Callable接口

(4)创建线程池

3.解决线程安全问题

(1)同步代码块

        例:卖票

(2)同步方法

        例:卖票

(3)显示锁ReentrantLock

        例:卖票

4.多线程间的通信

(1)生产者、消费者问题

(2)死锁


1.线程的相关概念

(1)进程、线程

        进程:是正在运行的应用程序,拥有独立的内存单元,是资源分配的基本单位;

        线程:线程是进程的一条执行路径,多个线程共享内存资源,是cpu调度的基本单位;

        具体来说,每个线程都有自己的栈空间,但共享一个堆内存;

(2)并行、并发

        并行:多个CPU同时执行多个任务。      

        并发:一个CPU(采用时间片)同时执行多个任务。

(3)线程的生命周期

       新建New  已经创建好线程,但还没有启动;

       可运行Runnable  正在等待CPU时间片(Ready)或者正在运行(Running)

       阻塞Blocking 等待获取一个排它锁,如果其他线程释放了锁就会结束此状态

       无限期等待Waiting 等待其他线程显示地唤醒 Object.notify() notifyAll() 也可能是因为join进入等待的,那就要等被调用的线程执行完毕

       限期等待Timed Waiting 一定时间后系统自动唤醒 Object.wait()或join()设置了时间参数的,或者sleep()

        死亡terminated 线程完成任务自动结束,或产生异常而结束 

        

2.多线程的实现方式

(1)继承Thread类

        继承Thread类,重写run方法,方法内是线程要执行的操作;调用start方法启动线程

        Thread t2 =new Thread(){
            @Override
            public void run() {
                //线程要执行的操作
            }
        };
        t2.start();

(2)实现Runnable接口

        创建MyRunnable类实现Runnable接口,重写run方法,实例化对象,传给Thread构造器,创建线程;Runnable接口的实现类相当于一个任务,把任务对象传递给线程,由线程执行任务

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                //要执行的操作
            }
        });
        t2.start();

(3)实现Callable接口

        创建MyCallable类实现Callable接口,重写call方法,实例化对象,传给FutureTask构造器,创建任务对象,并将任务对象传给Thread构造器,创建线程

Callable和Runnable的区别:Callable有返回值,返回值类型定义为泛型,返回值由FutureTask封装,通过调用FutureTask对象的get方法获得返回值

    FutureTask<Integer> task = new FutureTask(new Callable<Integer>(){
        @Override
        public Integer call(){
            //操作
            return res;
        }
    });

    Thread t1 = new Thread(task);
    t1.start();
    Integer res = task.get();

(4)创建线程池

        线程池中提前创建了多个线程,如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务。避免了频繁的创建和销毁线程。

ExecutorService service = Executors.newFixedThreadPool(3);
service.execute(new Task());//分配任务并执行任务
service.shutdown();

class Task implements Runnable{
    @Override
    public void run(){
        //操作
    }
}

3.解决线程安全问题

(1)同步代码块

        把操作共享数据的代码都放在同步代码块内;锁是唯一的,所有线程共用一把锁;

synchronized(锁){
    //操作数据
}

        例:卖票

        对共享数据ticket的操作包括if判断和ticket--;所以把整个if-else语句包在synchronized里面,不能把while循环包进去,否则一个线程拿到锁后会卖完所有票才释放。

class Ticket1 implements Runnable{
    private int ticket=10;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                if(ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket--;
                    System.out.println("余票:"+ticket);
                }else {
                    break;
                }
            }
        }
    }
}

(2)同步方法

        把方法加上synchronized关键字修饰,默认的锁是this或者当前类

public synchronized void fun(){
    
}

        例:卖票

class Ticket2 implements Runnable{
    private int ticket = 10;
    @Override
    public void run() {
        while (true){
            boolean flag=print();
            if(!flag){
                break;
            }
        }

    }
    public synchronized boolean print(){
        if(ticket>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticket--;
            System.out.println("余票"+ticket);
            return true;
        }else{
            return false;
        }
    }
}

(3)显示锁ReentrantLock

        在操作共享数据的前后分别加锁、解锁

ReetrantLock l = new ReentrantLock();

l.lock();
//操作
l.unlock();

        例:卖票

class Ticket3 implements Runnable{
    private int ticket = 10;
    private ReentrantLock lk = new ReentrantLock();
    @Override
    public void run() {
        while (true){
            lk.lock();
            try {
                if(ticket>0){
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ticket--;
                    System.out.println("余票"+ticket);
                }else{
                    break;
                }
            }finally {
                lk.unlock();//将解锁放到finally块中,否则可能因为break提前退出,导致锁无法释放
            }
        }
    }
}

4.多线程间的通信

(1)生产者、消费者问题

        若物品达到最大容量,生产者自动wait(),等待消费者取走物品后,被notify()唤醒,再生产

        若当前没有物品可消费,消费者自动wait(),等待生产者产出物品后,被notify()唤醒,再消费

               说明:wait常用于以下情境:

                        thread1: if(条件不满足) wait() 挂起当前线程,此时会释放锁

                        其他thread: do something …… 使得条件满足,notify() 唤醒线程1

                注:sleep和wait的区别

                        sleep是Thread类的静态方法,wait是Object类的方法

                        sleep不会释放锁,wait会释放锁(如果没有释放锁,那么其它线程就无法进入对象

                        的同步方法或者同步控制块中,那么就无法执行 notify() 或者 notifyAll() 来唤醒挂起

                        的线程,造成死锁)

(2)死锁

        两个或多个线程,由于资源竞争或通信造成的一种阻塞现象,永远在互相等待,无法推进。

        死锁的条件:互斥访问共享资源、保持和请求、不可剥夺、循环等待

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值