多线程

创建线程的两种方法:

1-可以通过继承Thread来创建线程(类名 extends Thread)
  • 重写run(),设置线程任务。实现时,直接通过类名创建对象,然后对象.start(),就默认运行线程里的run方法
2-可以实现Runnable接口(类名 implements Runnable)
  • 需要重写run方法,run()里面不能有参数

线程最后启动步骤(实现):

  • ①创建Runnable对象(前提是有类实现了Runnable接口)
  • ②创建一个Thread对象,然后把Runnable的对象名当作参数传进去传进去
注意:Runnable里面没有start()方法,所以需要借助Thread类,用Thread类的对象调用start()。
接口实现的好处(以后常用):
  • ①可以“多继承”,在继承了其他类以后,还可以通过接口创建线程。
  • ②把创建线程和运行线程分离:先创建接口对象,再把对象名传到Thread()里面,再start(),运行
注意:
  • 如果在创建线程后,运行run(),而不是start(),则变成main的单线程。

  • 每一个start()都会开辟一个新的栈空间,运行run()方法。CPU对线程的运行选择就是在选择不同的栈空间。

  • 创建的线程对象会放在堆内存里。

匿名内部类的使用:(new一个父类Thread或者new Runnable()接口类)

//①对于继承Thread的:  
new Thread()
         {public void run(){//重写方法}
    }.start();
//②对于接口实现的:
Runnable r=new Runnable()
               {//重写run方法方法};
//运行线程:
new Thread(r).start();
//或者直接用方法体代替方法名:
new Thread(实现类过程new Runnable()
                              {//重写方法}
                     ).start() ;
//就是把实现接口的整个方法当作一个对象名
  • 对于实现类比继承类:实现类可以创建“一个实现对象,传到多个线程里(多个Thread()传参)”,实现多个线程共享一段实现类里的run()里的资源

线程安全问题:

  • 当多个线程访问同一段(共享)代码块时,刚好里面又发生wait或者sleep等让当前线程失去cpu执行权的操作时,让其他的线程只能等待当前线程操作完,才能进入代码块。
解决线程安全问题的方法:
①同步代码块 (放在run()里面)
      synchronized(同步锁){
        需要同步的代码
 }

同步锁:只是一个概念,可以是任意类型(的对象),多个线程需要使用同一把锁

锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行
  • (当第一个线程遇到同步代码块,会检查同步代码块有没有锁对象,如果有,就进入同步代码块,第二个线程遇到同步代码块发现没有锁对象,就进不去同步代码块,只有等待(进入阻塞状态)第一个线程完成同步代码块任务,释放锁对象,第二个及后面的线程才能继续抢锁对象。)

  • (保证了共享数据的安全,但是线程频繁的判断锁,获取锁,释放锁,会降低程序效率)

②同步方法
    把访问共享数据的代码抽取出来,放在一个方法中,在方法上添加synchronized。
    public synchronized void  text(){
         共享数据代码
      }
  • 也有锁对象,只不过是隐藏的this,谁调用,锁谁,锁的是当前进入同步代码块的线程的对象

  • 也可以使用静态同步方法,即在上面的代码示例中,加入static,如果存在调用函数或函数内变量,则函数及变量也需要加static,静态只能调用静态的。

  • 静态方法的锁对象不是this,this是创建对象后产生的,而静态方法优先于对象,还没有创建对象,就已经在内存中了。

  • 静态方法的锁对象是本类的class属性—class文件对象(反射)
    synchronized(Runnable1.class){
    同步代码块
    }

③锁机制(Lock锁(接口))
  • 实现提供了比使用synchronize方法和语句可获得更广泛的锁定操作。

  • void lock()获取锁 和 void unlock()释放锁

  • 因为是个接口,但有一个类实现了lock方法叫ReentrantLock。

  • 使用步骤:

    • ①在成员位置创建一个ReentrantLock对象。
      Lock l = new ReentrantLock;(多态)
    • ②在可能出现安全问题的代码前调用Lock接口中的lock获取锁。
      l.lock();
    • ③在可能出现安全问题的代码后调用Lock接口中的unlock释放锁。
      l.unlock();
      也可以在最后释放锁的时候,把unlock()放在finally{l.unlock()}里面,这样无论前面代码有没有问题,最后都释放锁。
  • 在一个代码块中,如果被锁住了,是可以放弃cup,让其他线程运行其他代码块的,只要没有其他线程进入当前被锁的代码块就没问题
    区分是多个线程抢夺一个代码块和多个线程各干各的代码块,只是需要抢夺cpu的问题。

  • cpu随便抢,改变的只是执行顺序,但是同步代码块(共享数据)如果被多个线程抢占,则会改变里面数据。
    即使在上了锁的代码块里面,也可以让出cpu

  • 代码块加锁,是防止线程让出cpu的时候,被其他想要进入当前代码块的线程趁虚而入,而不是防止其他线程进入其他代码块

线程状态:

在这里插入图片描述

  • sleep(number):计时等待 (静态方法)

  • wait():无限等待

  • notify():一次只能唤醒一个线程,当有多个线程时,随机唤醒。

  • notifyAll:唤醒所有线程。

  • wait()和notify()只能让一个运行:一个等待一个唤醒,所以必须把他们放在同步代码块或同步方法中,且保证锁对象唯一,即同一个锁对象调用同一对wait()或者notify()。
    wait()和notify()属于Object类的方法:因此锁对象可以是任意的,而任意对象的所属类都继承了Object类。

  • 对于实现类比继承类:实现类可以创建“一个实现对象,传到多个线程里(多个Thread()传参)”,实现多个线程共享一段实现类里的run()里的资源

线程池

  • java.util.concurrent.Executors:线程池的工厂类,用来生成线程池。
  • Executors类中的静态方法:
//创建一个可重用固定线程数的线程池
static ExecutorService newFixedThreadPool(int n Threads)  
// int nThreads :创建线程池中包含的线程数量 
//返回值:ExecutorService(是一个)接口,返回的是ExecutorService接口实现类对象。可以使用ExecutorService接口接收(面向接口编程)
(用一个线程池对象接收一个指定线程数量的线程池)(用接口创建对象接收线程池)
  • 用从线程池中获取线程,调用start(),执行线程任务
    submit(Runnable task) 提交一个Runnable任务用于执行
  • 关闭/销毁线程池的方法
    void shutdown()
线程池的使用步骤:
  • 使用线程池的工厂类Executor里面提供的静态方法newFixedThreadPool生产一个指定线程数量的线程池

  • 创建一个类,实现Runnable接口,重写run方法,设置线程任务

  • 调用ExecutorService中的submit,传递线程任务(实现类),开启线程,执行run方法

  • 调用ExecutorService中的方法shutdown销毁线程池(不建议执行)

  • ExecutorService object=Executor.newFixedThreadPool(nThread:2)
    用接口创建一个对象,接收通过Executor工厂类里的newFixedThreadPool()方法创建的指定数量的线程池。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值