多线程

 一、基本概念

程序:为完成特定任务、用某种语言编写的一组指令的集合。通俗来讲,一段静态的代码就是一个程序

进程:程序的一次执行过程,或是正在运行的一个程序,我们将代码运行起来,代码就变成了一个进程

线程:进程可进一步细化为线程,是一个程序内部的一条执行路径,若一个进程同一时间并行执行就变成了多线程 (线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc))

 

并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事

并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事

二、创建的多线程的方法

java.lang.Thread类

1、方法一:继承于Thread类

             1)重写Thread的run方法

             2)创建Thread类的子类的对象

             3)通过次此对象调用start() 方法

注意:方法一中我们不能直接调用run() 方法的方式启动线程,需要通过此线程对象调用start() 方法,对于一个已经调用过start() 方法的线程不可以再次调用start()  ,如果需要一个新的线程,需要重新创建一个线程对象;

2、线程中的常用方法:

            1)start(): 启动当前线程,调用当前线程的run()  方法

            2)  run(): 通常需要重写该方法,将线程中需要执行的方法定义到该方法内部

            3)  currentThread() : 获取当前线程的名字

            4)setName : 设置当前线程的名字

            5)getName : 获取当前线程的名字

            6)yield() : 线程让步

            7)join() : 当程序执行中调用其他线程的join方法,调用线程将被阻塞,直到join() 方法加入的join线程执行完为止

            8)sleep() :当前线程睡眠

3、线程的调度

            1) 抢占式: 高优先级的线程抢占CPU,同优先级的先进先出,采用时间片的方式

            2) 线程的优先级: MAX_PRIORITY:10     MIN _PRIORITY:1    NORM_PRIORITY:5 

                      2-1、getPriority() :返回线程优先值       setPriority(int newPriority) :改变线程的优先

4、方法二、实现Runnable接口

            1)创建一个实现了Runnable接口的类

            2)实现类去实现Runnable中的抽象方法:run()

            3)创建实现类的对象

            4)将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象

            5)通过Thread类的对象调用start()

比较: 优先选择实现Runnable接口的方式,

           1)实现的方式避免了单继承的局限性

           2)实现的方式更适合来处理多个线程有共享数据的情况

联系:两种方式都需要重写run方法

5、线程的生命周期                  

                                                                       

 

6、线程的同步

          问题的引入: 多个线程执行的不确定性会导致执行结果的不稳定,例如多个用户操作同一份数据,会导致数据的破坏,故我们通过同步机制,来解决线程的安全问题

          1)、方式一: 同步代码块 {  synchronized(同步监视器)  }

                    说明:操作共同数据的代码,就是需要被同步的代码

/*
synchronized(同步监视器) {   
//同步监视器就是一把锁 ,任何一个类的对象,都可以充当锁,但是所有的线程必须使用同一把锁
//  实现的方式可以使用this充当锁或者 类.class

    //操作同步数据的代码块
}
*/
Object obj = new Object();

synchronized(obj) { 
    //操作同步数据的代码块
}

         局限性:同步代码块的方式,解决了线程的安全问题,但是操作同步代码时,只能一个线程参与,其他线程等待,这样就相当于单线程了,效率低

         2)、方式二: 同步方法

                  将同步代码块的代码提取到一个方法中,给方法添加synchronized

         死锁问题:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁问题

                  尽量减少同步资源的定义,避免嵌套同步

         3)、方法三:Lock锁 -- > JDK5.0 提供 ,为解决线程安全问题的一种定义方式

private ReentrantLock lock = new ReentranLock();

try {
    lock.lock();

    // 操作同步数据的代码
    
} finally { 
    lock.unlock();
}

         Lock锁和synchronized  的不同:Lock锁是显示锁,我们需要手动的打开锁,手动的关闭锁,synchronized 相当于是一个隐式锁,出了作用域会自动释放锁

         4)、线程的通信

                 例子: 使用两个线程打印1-100,线程一和线程二交替打印

                  涉及到的三个方法( 必须使用再同步代码块和同步方法中 ): wait():一旦执行此方法,当前线程就会进入阻塞状态,并释放同步监视器

                                                    notify():  一旦执行此方法,就会唤醒被wait的一个线程,按照优先级的高低来唤醒

                                                    notifyAll():  一旦执行此方法,就会唤醒所有被wai的线程

class MyThread implements Runnable {

    private int number = 1;

//notify(),notifyAll(),wait() 三个方法的调用者必须是同步代码块或者同步方法中的同步监视器

    @Override
    public void run() {
        /*打印1-100,两个线程交替打印*/
        while (true) {
            synchronized (this) {
                this.notify();
                if (number <= 100) {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + ":" + number);
                    number++;
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    break;
                }
            }
        }
    }
}
public class ThreadTest {
    public static void main(String[] args) {

        MyThread myThread1 = new MyThread();

        Thread t1 = new Thread(myThread1);
        Thread t2 = new Thread(myThread1);

        t1.setName("线程1");
        t2.setName("线程2");
        
        t1.start();
        t2.start();

    }
}

          sleep()  和   wait()  方法的异同:① 两者被执行后都会使当前线程进入阻塞状态; ② sleep() 可以在任何需要的地方调用,而wait() 必须使用在同步代码块中调用  ③  都在同步代码块中使用,sleep() 不会释放同步监视器, wait()  会释放同步监视器 

7、方式三: 实现Callable接口   JDK 5.0 新增

                  1) 创建一个Callable的实现类

                  2) 实现call方法,将需要执行的操作声明在call中

                  3)创建Callable的实现类的对象

                  4)将Callable实现类的对象传递到FutureTask构造器中,创建FutureTask的对象

                  5)将FutureTask的对象作为参数传递到Thread的构造器中,创建Thread对象,并调用start()

                  6) 如果想要cal方法的返回值,可以通过FutureTask的对象调用get()

         为什么实现Callable 接口 比 实现Runnable 接口 方式强大: ① 很显然实现Callable接口重写的 call()  可以拿到返回值 ②  call()  方法可以抛异常,被外面的操作捕获,获取异常信息  ③ Callable 支持泛型

8、方式四: 使用线程池

         线程池为我们提前创建了多个线程,放入线程中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁、实现重复利用

//提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);

//执行
service.execute(Runnable接口实现类对象);
//service.submit()   Callable接口实现类

//关闭连接池
service.shutdown();

 

 

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值