多线程

多线程

多线程:我认为就是多条执行路径

原来程序的执行是一条路径走到底,现在多线程是多个路径同时走,提高了程序的运行效率,程序原来是一步步的执行,现在可能有一个以上的线程并发运行,具体哪一个我们不知道。

线程:

线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作
单位。一条线程指的是进程中一个单一顺序的控制流。

  1. 一个进程中的线程共享相同的内存单元/内存地址空间可以访问相同的变量和对象,而且它们从同一堆中

    分配对象通信、数据交换、同步操作

  2. 一个进程中可以并发多个线程,每条线程并行执行不同的任务。

  3. 多线程是指在同一进程下,充分利用资源,多条执行路径,共享资源。

线程的创建

1.继承Thread类实现

  1. 创建线程类: 继承 Thread类 +重写 run() 方法
  2. 构造线程类对象: 创建 子类的对象
  3. 启动线程: 通过子类对象调用 start()方法
public class TestThread {
public static void main(String[] args) {
    // 创建线程类对象
	SomeThread oneThread = new SomeThread();
    // 启动线程
    oneThread.start();
}
}
// 创建线程类
class SomeThead extends Thread{
@Override
  public void run()
 {
  //do something here
 }
}

**该方法的特点:**如果当前类已经有继承父类,那么其无法继承Thread类,run()方法中出现的异常只能捕获,不能抛出。

2.实现Runable接口

  1. 通过类实现Runable接口,重写run()方法
  2. 创建一个实现类对象,利用实现类对象创建一个Thread类对象
  3. 启动线程
public class ThreadDemo implements Runnable{

    @Override
    public void run() {
        for(int i  = 1;i<=20;i++){
            System.out.println("一边看电视.....");
            try {
                // 线程休眠
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args) {
        ThreadDemo demo = new ThreadDemo();
        //1.创建线程
        Thread th = new Thread(demo);
        //2.开启线程
        th.start();

        for(int i  = 1;i<=20;i++){
            System.out.println("一边吃零食.....");
            try {
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

3.实现Callable接口

  1. 创建实现 Callable 接口的实现类 + 重写 call() 方法
  2. 创建一个实现类对象
  3. 由 Callable 创建一个 FutureTask 对象
  4. 由 FutureTask 创建一个 Thread 对象
  5. 启动线程

一个简易版龟兔赛跑的多线程代码:

public class TestNewThread implements Callable {
    /**
     * 创建一个Lock实例对象,实现加锁lock(),释放锁unlock()
     * */
    private Lock lock = new ReentrantLock();

    @Override
    public Integer call(){
        try {
            this.method(Thread.currentThread());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return -1;
    }

    /**
    * @Description: 线程加锁测试
    * @Param: [t]
    * @Return: void
    **/
    private void method(Thread t) throws InterruptedException {
        // 加锁,lock()方法后面必须紧跟try/catch语句块,中间不能出现抛出异常或者
        // 使得代码无法运行到finally语句块的语句块

        // lock.lock();无法获取资源就一直等待
        // trylock()方法,返回一个Boolean,无法获取资源就返回false。可以设置其等待时间
        if(lock.tryLock(20, TimeUnit.SECONDS)){
            try {
                System.out.println("锁"+t.getName()+"开启");
                // 休眠 2 秒
                Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 释放锁,unlock()必须在finally语句的第一行,先解锁,其它线程才可以使用资源
                lock.unlock();
                System.out.println("锁"+t.getName()+"释放");
            }
        }

    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {

		// 创建一个实现Callable接口的类实例
        TestNewThread t = new TestNewThread();

        // 使用FutureTask对象调用get()方法获取call方法返回的值
        FutureTask ft = new FutureTask(t);

        Thread th1 = new Thread(ft);
        //Thread th2 = new Thread(ft);// 只会运行上一个线程
		// 通过实现Callable接口来创建线程,无法实现多线程的功能,只能是单线程的运行
        th1.start();
        //th2.start();

        // call方法返回值
        Object o = ft.get();
        System.out.println(Thread.currentThread().getName()+"=="+o);// 输出结果:main=-1
    }
}

4.使用线程池来创建线程

使用线程池的方式:

背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

思路:提前创建好多个线程,放入线程池之,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。(数据库连接池)

好处

  1. 提高响应速度(减少了创建新线程的时间)
  2. 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
  3. 便于线程管理

corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止

JDK 5.0 起提供了线程池相关API:ExecutorService 和 Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor.
void execute(Runnable coommand):执行任务/命令,没有返回值,一般用来执行Runnable
Future.submit(Callable task):执行任务,有返回值,一般又来执行Callable
void shutdown():关闭连接池。

Executors工具类,线程池的工厂类,用于创建并返回不同类型的线程池
Executors.newCachedThreadPool()创建一个可根据需要创建新线程的线程池
Executors.newFixedThreadPool(n)创建一个可重用固定线程数的线程池
Executors.newSingleThreadExecutor():创建一个只有一个线程的线程池
Executors.newScheduledThreadPool(n)创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
public class TestNewThread implements Callable {
    /**
     * 创建一个Lock实例对象,实现加锁lock(),释放锁unlock()
     * */
    private Lock lock = new ReentrantLock();

    @Override
    public Integer call(){
        try {
            this.method(Thread.currentThread());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return -1;
    }


    /**
    * @Description: 线程加锁测试
    * @Param: [t]
    * @Return: void
    **/
    private void method(Thread t) throws InterruptedException {
        // 加锁,lock()方法后面必须紧跟try/catch语句块,中间不能出现抛出异常或者
        // 使得代码无法运行到finally语句块的语句块

        // lock.lock();无法获取资源就一直等待
        // trylock()方法,返回一个Boolean,无法获取资源就返回false。可以设置其等待时间
        if(lock.tryLock(20, TimeUnit.SECONDS)){
            try {
                System.out.println("锁"+t.getName()+"开启");
                Thread.sleep(2000);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                // 释放锁,unlock()必须在finally语句的第一行,先解锁,其它线程才可以使用资源
                lock.unlock();
                System.out.println("锁"+t.getName()+"释放");
            }
        }

    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {

        // 1、创建固定线程个数为十个的线程池
        ExecutorService es = Executors.newFixedThreadPool(10);
        // 2、new一个Runable或Callable接口的对象
        TestNewThread t = new TestNewThread();
        // 3、执行线程,最多十个
		/*        
		es.execute(Runable r);常用来执行实现Runable接口的对象
		submit(Callable<t> c)常用来实现Callable接口的对象
		System.out.println(es.submit(t));返回一个FutureTask对象
		*/
        Future ft = es.submit(t);
        System.out.println(Thread.currentThread().getName()+"="+ft.get());// 输出:main=-1
        // 4、关闭线程池
        es.shutdown();


    }
}

线程的状态

  1. 新建状态
    使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状
    态直到程序 start() 这个线程。

个人理解:就是new一个Thread对象出来

  1. 就绪状态
    当线程对象调用了start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待
    JVM里线程调度器的调度。

  2. 运行状态
    如果就绪状态的线程获取 CPU 资源,就可以执行run(),此时线程便处于运行状态。处于运行状态的线
    程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。

  3. 阻塞状态
    如果一个线程执行了sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行
    状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。

可以分为三种:

等待阻塞:行状态中的线程执行 wait() 方法,使线程进入到等待阻塞状态。

同步阻塞:线程在获取 synchronized同步锁失败(因为同步锁被其他线程占用)。

其他阻塞:通过调用线程的 sleep()join() 发出了 I/O请求时,线程就会进入到阻塞状态。

当sleep() 状态超时,join() 等待线程终止或超时,或者 I/O 处理完毕,线程重新转入就绪状态。

  1. 死亡状态
    一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。

     注意:
         一个线程如果进入阻塞状态,阻塞解除没有办法直接恢复到运行,会直接恢复就绪
         一个线程如果一旦终止,没有办法恢复
    

sleep线程休眠|睡眠

参数:毫秒ms

  1. 是一个静态方法
  2. 带着资源一起休眠
  3. 当一个执行到sleep方法,当前线程就会休眠指定时间,在休眠过程中,让出cpu的资源

礼让线程yield:
让出cpu的资源,同时恢复就绪状态,等待cpu重新调度,是否还是会调度到原线程,不一定

插队线程join
void join() 等待该线程终止。
让当前线程进入阻塞状态

判断线程状态Thread.State
getState() 获取线程状态 返回值是一个枚举类型

线程优先级

​ 每一个线程都有优先级别问题 优先执行谁 ,概率问题,但是 不能控制一个线程是否先执行
1~10
1为最小 10为最大 默认为5
static int MAX_PRIORITY
线程可以具有的最高优先级。
static int MIN_PRIORITY
线程可以具有的最低优先级。
static int NORM_PRIORITY
分配给线程的默认优先级。

 getPriority() 获取优先级
 setPriority() 设置优先级

线程安全

线程同步:即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态。

同步就是协同步调,按预定的先后次序进行运行。如:你说完,我再说。

“同”字从字面上容易理解为一起动作,其实不是,“同”字应是指协同、协助、互相配合。
在Java里面,通过 synchronized 进行同步的保证。它包括两种用法:synchronized 方法和 synchronized

死锁

死锁是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的线程称为死锁线程。

// 口红
class Lipstick{
}
// 镜子
class Mirror{
}
class Makeup extends Thread {
  int flag;
  String girl;
  static Lipstick lipstick=new Lipstick();
  static Mirror mirror= new Mirror();
  @Override
  public void run() {
    // TODO Auto-generated method stub
    doMakeup();
 }
  void doMakeup(){
    if(flag==0){
      synchronized (lipstick) {
        System.out.println(girl+"拿着口红!");
        try {
          Thread.sleep(1000);
       } catch (InterruptedException e) {
          e.printStackTrace();
       }
        synchronized (mirror) {
        
		 System.out.println(girl+"拿着镜子!");
       }
     }
   }
     else{
      synchronized (mirror) {
        System.out.println(girl+"拿着镜子!");
        try {
          Thread.sleep(2000);
       } catch (InterruptedException e) {
          e.printStackTrace();
       }
        synchronized (lipstick) {
        
 		System.out.println(girl+"拿着口红!");
       }
     }
   }
 }
}
public class TestDeadLock {
  public static void main(String[] args) {
    Makeup m1 = new Makeup();  m1.girl="大丫"; m1.flag=0;
    Makeup m2 = new Makeup();  m2.girl="小丫"; m2.flag=1;
    m1.start();
    m2.start();
 }
}

如何解决死锁问题:

  1. 往往是程序逻辑的问题。需要修改程序逻辑。
  2. 尽量不要同时持有两个对象锁。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值