Fork me on GitHub

多线程

一,并发和并行

    .并行:在同一时刻,有多个指令在多个cpu上同时执行

    .并发:在同一时间,有多个指令在一个cpu上交替执行

 

二,进程和线程

    进程:是正在运行的软件

    .独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度独立单位。

    .动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡。

    .并发性:任何进程都可以同其他进程一起并发执行

 

三,线程:是进程中的单个顺序控制流,是一条执行路径

      .单线程:一个进程如果只有一条执行路径,则称为单线程程序

 

四,多线程的实现方案

    .继承Thread类

    .实现Runnable接口的方式进行实现

    .利用callable和future接口方式实现

   两个小问题?

      .为啥要重写run()方法

       -----因为run()是用来封装被线程执行的代码

     .run()方法和Start()方法的区别?

      -----run():封装线程执行的代码直接调用,相当于普通方法的调用,并没有开启线程

      -----start():启动线程然后由JVM调用此线程的run()方法

   方法2.实现Runnable接口

   .定义一个类MyRunnable类实现Runnable接口

   .在MyRunnable中重写run()方法

   .创建MyRunnable类的对象

   .创建Thread类的对象,把MyRunable对象作为构造方法的参数

   .启动线程

  方式3:实现callble和Future

    .定义一个MyCallable实现Callabke接口

    .在Mycallable中重写call方法

    .创建Mycallable类的对象

    .创建Future的实现类FutureTask对象,把Mycallable对象作为构造方法的参数

    .创建Thread类的对象,把FutrueTask作为构造方法的参数

    .启动线程

    .再调用get方法(),就可以获取线程结束后的结果

 

三种方式的对比

  优点 缺点
实现Runnable,callable接口 扩展性强,实现接口的同时还可以继承其他类 编程复杂,不能直接使用Thread类中的方法
继承Thread接口 编程简单,可以直接使用Thread类中的方法 可扩展性差,不能再继承其它类

 

Runnable

package com.guancun.thread.myrunnable;


//实现runnable接口创建线程
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <100 ; i++) {
            System.out.println(Thread.currentThread().getName()+"开启了"+i+"条线程!!");
        }
    }

    public static void main(String[] args) {
        //创建参数对象
           MyRunnable myRunnable = new MyRunnable();
           //创建了一个线程对象,并将参数传递给这个线程
        Thread thread1 = new Thread(myRunnable);
        Thread thread2 = new Thread(myRunnable);

        thread1.start();
        thread2.start();

    }
}
Callable
package com.guancun.thread.mycallable;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class MyCallable implements Callable<Object> {
    @Override
    public Object call() throws Exception {
        for (int i = 0; i < 100; i++) {
            System.out.println("表白" + i);
        }
        return "答应了";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建Mycallable对象,线程开启后要执行里面的call()方法
        MyCallable myCallable = new MyCallable();
        //可以获取线程执行完毕后的结果,也可以作为参数传递给Thread对象
        FutureTask futureTask = new FutureTask(myCallable);

        //创建线程对象
        Thread thread = new Thread(futureTask);
        //开启线程
        thread.start();
        System.out.println(futureTask.get());
    }
}

 

Thread

package com.guancun.thread.mythread;

public class MyThread extends Thread{
    //重写run方法

    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            System.out.println("开启了"+i+"条线程!!");
        }
    }
}











package com.guancun.thread.mythread;

public class DemoTest {
    public static void main(String[] args) {

        MyThread myThread1 = new MyThread();
        MyThread myThread2 = new MyThread();
        //myThread1.run();表示创建对象调用方法,没有开启线程。
        myThread1.start();
        myThread2.start();
    }
}

四,获取和设置线程名称

      获取线程的名字

      .String getName():返回此线程的名称

    Thread类中设置线程的名字

     .void setName(String name):将此线程的名称更改为name

      .通过构造方法也可以设置线程名称

 

五,获取线程的对象

   .public static Thread currenthread()返回当前正在执行的线程的引用

 

六,线程休眠

  .public static void sleep(Long time):让线程休眠指定时间(单位:毫秒)

 

七,线程调度

        多线程的并发运行

               计算机中在任何时刻只能执行一条机器指令,每个线程只有获得cpu的使用权才能执行代码,各个线程轮流获得cpu的使用权,分别执行各自的任务

 

 

八, 线程有两种调度模型

               .分时调度模型:所有线程轮流使用cpu的使用权,平均分配每个线程使用cpu的时间片

               .抢占式调度模型:优先让优先级高的线程使用cpu,如果线程的优先级相同,那么会随机选择一个优先级高的线程获取cpu时间片相对多一些

 

 

九,线程的优先级(优先级越高,抢到使用权越大)

     .public final void setPriority(int newPriority)   设置线程优先级

     .public final int getPriority()           获取线程优先级

 

 

十,后台线程/守护线程

    .public final void setDaemon (bolean on):设置为守护线程

package com.guancun.thread.protectthread;

public class ProtectThread1 implements Runnable{

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"@@@"+i);
        }
    }
}









package com.guancun.thread.protectthread;

public class ProtectThread2 implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"@@@"+i);
        }
    }
}



















package com.guancun.thread.protectthread;

public class ProtectThread implements Runnable{
    public static void main(String[] args) {

        ProtectThread1 T1 = new ProtectThread1();
        ProtectThread2 T2 = new ProtectThread2();
        Thread t1 = new Thread(T1);
        Thread t2 = new Thread(T2);
        t1.setName("女神");
        t2.setName("备胎");
       t2.setDaemon(true);
       t1.start();
       t2.start();
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+"@@@"+i);
        }
    }
}

 

 

 

 

十一,线程安全

    买票案例

    .多线程操作共享数据

  如何解决线程安全问题

   .基本思想:让程序没有安全问题的环境

   怎么实现?

    .把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个执行即可。

   同步代码块

        锁多条语句操作共享数据,可以使用同步代码块实现

            .格式

                     synchronized(任意对象){

                                                            多条语句操作共享数据的代码

                                                                                   }

            .默认情况下锁是打开的,只要有一个代码进去锁就关闭

           .当线程执行完出来锁才会打开

   同步锁的好处和坏处

         .好处:解决了多线程数据安全问题

         .坏处:耗费资源,降低程序运行效率率

  同步方法

       同步方法:就是把synchronized关键字加到方法上

             .格式: 

                       修饰符  synchroized  返回值类型  方法名(方法参数)()

  同步代码快和同步方法区别

            .同步代码块可以锁指定代码,同步方法是锁住方法中所有代码

            .同步代码块可以指定锁对象,同步方法不能指定锁对象

 

  同步方法的锁对象是什么?

           .this

   

   同步静态方法:就是synchronized关键字加到静态方法上。

    .格式:

               修饰符  static Synhronized 返回值类型方法名(方法参数){} 

    同步静态方法的锁对象是什么?

         类名.class

 Locks锁

        JDK5以后提供的一个新的锁对象Lock.

         Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作

         Lock中提供了获得锁和释放锁的方法

          void lock ():获得锁                    .void unlock ():释放锁

        Lock 是接口不能实例化,这里采用它的实现类ReentrantLock来实例化

        

        死锁:线程死锁是由于两个或者多个线程互相持有对方所需资源,导致这些线程处于等待状态,无法前往执行

 

       生产者和消费者

              等待和唤醒方法

                   为了体现产生和消费过程中的等待和唤醒,Java就提供,了几个方法供我们使用,这几个方法在object类中,Object类的等待和唤醒方法;

方法名 说明
void wait () 导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifall
void  notify() 唤醒正在等待对象监视器的单个线程
void  notifyAll 唤醒正在等待对象监视器的所有线程

         

package com.guancun.thread.producers_consumers;

public class Desk {
    //false桌子上没有食物,反之TRUE有
    public  static  boolean flag = false;
    //生产数量
    public  static  int count = 10;
    //
    public  static  final  Object lock = new Object();
}








package com.guancun.thread.producers_consumers;
//生产者
public class Cooker extends  Thread{
    @Override
    public void run() {
        while (true){
            synchronized (Desk.lock) {
                //判断做了几个食物了
                if (Desk.count == 0) {
                    break;
                } else {
                    if (Desk.flag) {
                        //厨师等待
                        try {
                            Desk.lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println("厨师正在加工食物");
                        Desk.flag = true;
                         Desk.lock.notifyAll();
                    }
                }
            }
        }
    }
}














package com.guancun.thread.producers_consumers;
//消费者
public class FoodDie extends Thread{
    @Override
    public void run() {
           while (true){
               synchronized (Desk.lock){
                   if (Desk.count==0){
                       break;
                   }else {
                       //判断桌子上是否有食物
                       if (Desk.flag){
                           System.out.println("消费者开始吃食物");
                           //吃完桌子变为false
                           Desk.flag = false;
                           //通知厨师再做
                           Desk.lock.notifyAll();
                           Desk.count--;
                       }else {
                           //消费者等待
                           try {
                               Desk.lock.wait();
                           } catch (InterruptedException e) {
                               e.printStackTrace();
                           }

                       }
                   }
               }
           }
    }
}
















package com.guancun.thread.producers_consumers;
//测试
public class DemoTest {
    public static void main(String[] args) {
        Cooker cooker = new Cooker();
        cooker.start();
        FoodDie foodDie = new FoodDie();
        foodDie.start();
    }
}

改进

package com.guancun.thread.producers_consumers;

public class Desk2 {
    //false桌子上没有食物,反之TRUE有
    private     boolean flag;
    //生产数量
    private    int count ;
    //
    private    final  Object lock = new Object();

    public Desk2() {
        this(false,10);
    }

    public Desk2(boolean flag, int count) {
        this.flag = flag;
        this.count = count;
    }

    public boolean isFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public Object getLock() {
        return lock;
    }
}













package com.guancun.thread.producers_consumers;
//生产者
public class Cooker2 extends  Thread{
    private  Desk2 desk;
    public Cooker2(Desk2 desk) {
             this.desk=desk;
    }
    @Override
    public void run() {
        while (true){
            synchronized (desk.getLock()) { 
                //判断做了几个食物了
                if (desk.getCount() == 0) {
                    break;
                } else {
                    if (desk.isFlag()==true) {
                        //厨师等待
                        try {
                            desk.getLock().wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else {
                        System.out.println("厨师正在加工食物");
                        desk.setFlag(true);
                         desk.getLock().notifyAll();
                    }
                }
            }
        }
    }
}












package com.guancun.thread.producers_consumers;
//消费者
public class FoodDie2 extends Thread{
    private  Desk2 desk;
    public FoodDie2(Desk2 desk) {
        this.desk=desk;
    }

    @Override
    public void run() {
           while (true){
               synchronized (desk.getLock()){
                   if (desk.getCount()==0){
                       break;
                   }else {
                       //判断桌子上是否有食物
                       if (desk.isFlag()){
                           System.out.println("消费者开始吃食物");
                           //吃完桌子变为false
                           desk.setFlag(false);
                           //通知厨师再做
                          desk.getLock().notifyAll();
                           desk.setCount(desk.getCount()-1);
                       }else {
                           //消费者等待
                           try {
                               desk.getLock().wait();
                           } catch (InterruptedException e) {
                               e.printStackTrace();
                           }

                       }
                   }
               }
           }
    }
}
















package com.guancun.thread.producers_consumers;
//测试
public class DemoTest2 {
    public static void main(String[] args) {
        Desk2 desk = new Desk2();
        Cooker2 cooker = new Cooker2(desk);
        cooker.start();
        FoodDie2 foodDie = new FoodDie2(desk);
        foodDie.start();
    }
}

 

            组塞队列实现等待唤醒机制

                 阻塞队列继承结构

                           Iterable ---------->collection---------->queue---------->BlockingQueue==============1.ArrayBlockingQueue实现类        2.LinkBlockingQueue实现类

                                接口                      接口                   接口                 接口

            组塞队列的等待唤醒机制

                  BlockingQueue的核心方法:

                                                 put(Object):将参数放入阻塞队列,如果放不进会阻塞

                                                 take():取出第一个数据,取不到会阻塞

                常见的BlockingQueue:

                                         ArrayBlockingQueue

                                                          ArrayBlockingQueue:底层数组,有界

                                         LinkBlockingQueue 

                                                          LinkBlockingQueue:底层链表,无界,但不是真正的无界,最大为int的最大值

                线程状态

              

 

 虚拟机中线程的六种状态

          新建状态(NEW)  -------------》 创建线程对象

          就绪状态(RUNNABLE)--------------> start方法

          阻塞状态 (BLOCKED) --------------> 无法获得锁对象

          等待状态(WAITING) ---------->  WAIT 方法

          计时等待 (TIMED_WAITING)----------->SLEEP方法

          结束状态  (TERMINATED)----------->全部代码运行完毕

       线程池

           步骤:

package com.guancun.thread.threadpool;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyTresdPool {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();//创建一个默认的线程池对象  大小为int最大值
        //Executors可以帮助我们创建线程池
        //ExecutorService可以帮助我们控制线程池
        executorService.submit(()->{

                System.out.println(Thread.currentThread().getName()+"在执行");


        });
        executorService.submit(()->{
            System.out.println(Thread.currentThread().getName()+"在执行i");
        });
        executorService.shutdown();
    }
}

     

    创建自定义线程池

                ThreadPoolExecutor    pool  = new  ThreadpoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);

                        参数解析:

                                        参数一:核心线程数量------》不能小于0    参数二:最大线程数------》不能小于0(最大数量》=核心数量)     参数三:空闲线程最大存活时间-----》不小于0     参数四:时间单位  ------》时间单位 

                                        参数五:线程队列-----》不为null      参数六:创建线程工厂------》不为null    参数七:任务拒绝策略------》不为null 

    任务拒绝策略

             ThreadPoolExecutor.Abortpolicy       丢弃任务并抛出RejecteddExecutionExecution异常。是默认策略。

             ThreadPoolExecutor.DiscardPolicy  丢弃任务但是不抛出异常这是不推荐的做法

             ThreadPoolExecutor.DiscardPolicy   抛弃队列中等待最久的任务然后把当前任务加入队列中

             ThreadPoolExecutor.CallerRunspolicy  调用任务的run()方法绕过线程直接执行

   

 

   Volatile关键字

             强制线程每次在使用的时候,都会看一下共享区最新的值

 

 

 

  原子性:

            所谓原子性是指在一次操作或多次操作中,要么所有的操作全部都得执行并且不会受到任何因素干扰而中断,要么所有操作都不执行,多个操作是一个不可分割的整体。

   

  原子类AtomicIntrger:

            构造方法:

                    public AtomicInteger():初始化一个默认参数值0的原子型Integer

                    public AtomicInteger():初始化一个指定值的原子型Integer

             成员方法:

int  get() 获取值
int  getAndIncrement() 以原子方式将当前值加1,注意这里返回的是自增前的值
int addAndGet() 以原子方式将当前值加1,注意这里返回的是自增后的值
int addAndGet(int data) 以原子方式将输入的数值与实例中得值(AtomicInteger)相加,并且返回结果
int getAndSet(int Value) 以原子方式设置为newValue的值,并返回值

     

          

   

 

posted @ 2021-06-15 14:12  风をした  阅读(59)  评论(0)    收藏  举报