线程

1、多线程

    1.1 线程与进程

      · 进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间,一应用程序可以同时运行多个进程;进程也是程序的一次执行过程,是系统运行程序基本单位;系统运行一个程序即是一个进程从创建,运行到消亡的过程。

      · 线程:线程是进程中的一个执行单元,负责当前进程中的执行,一个进程中至少有一个线程。一个进程中是可以多个线程的,这个应用程序也可以称之为多线程程序。

    1.2创建线程类

      Java 使用 Thread 类代表 线程 ,所有的线程对象都必须是Thread类或其子类的实列。每个线程的作用是完成一定的任务,实际就是执行一段程序流即一段顺序执行的代码

      1、定义Thread类的子类,并重写该类的run()方法,该run()方法的方法体就代表线程需要完成的任务,因此把run()方法称为线程执行体。

      2、创建Thread子类的实例,即创建了线程对象

      3、调用线程对象的start()方法来启动该线程

    

2、多线程详解

      2.1 Thread类

        构造方法:

        · public Thread() : 分配一个新的线程对象

        · public Thread( String name) : 分配一个指定名字的新的线程对象。

        · public Thread( Runnable target) : 分配一个带有指定目标的新线程对象。

        · public Thread( Runnable target,String name) : 分配一个带有指定目标的新线程对象并指定名字。

        常用方法:

        · public String getName() : 获取当前线程名称。

        · public void start() : 导致此线程开始执行;Java虚拟机调用此线程的run方法。

        · public void run() : 此线程要执行的任务在此处定义代码。

        · public static void sleep(long millis) : 使用当前正在执行的线程以指定的毫秒数暂停。

        · public static Thread currentThread() : 返回对当前正在执行的线程对象的引用

        

      测试类:

      

      自定义线程类:

      

      运行:

          

      2.2 Runnable类

          Runnable 也是非常常见的一种,我们只需要重写run方法即可。

          1、定义Runnable 接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程执行体。

          2、创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Theater对象才是真正的线程对象。

          3、调用线程对象的start()方法来启动线程。

            代码如下:

              

 

 

            

      3、线程安全

            如果有多个线程在同时运行,而这些线程可能会同时运行这段代码。程序每次运行结果和单线程运行的结果是一样的
        而且其他的变量的值也和预期的是一样的,就是线程安全的。

        实体类:

          

        测试类:

          

        运行结果:

          

        1、同样的票数,比如98这张票被卖了两次

        2、不存在的票,比如0票-1票,是不存在的

        这个问题,几个窗口(线程)票数不同步了,这种问题称为线程安全性问题。

      4、线性安全问题解决方法

        -- synchronized

          · 同步代码块:synchronized 关键字可以用于方法中的某个区块中,表示只对这个区块的资源实行互斥访问

          对于非static方法,同步锁就是this

          对于static方法,我们使用当前方法所在类的字节码对象(类名.Class)

          格式:

            

 

        

 

      -- Lock锁

        Lock机制提供了比 synchronized 代码块和 synchronized 代码块和 synchronized 方法更广泛的锁定操作,同步代码/同步方法具有的功能

        ·public void lock() : 加同步锁

        ·public void unlock() : 释放同步锁

        

    5、线程死锁

          1、当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。

          2、一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁

         

 

          

          

          

          执行结果:

          

           解决死锁:

             (1)减少同步代码块的嵌套

             (2)设置锁的超时时间

             (3)可以使用安全类jdk提高的安全类

                   

            执行结果:

            

      6、线程通信

            · 等待:

              · public final void wait ()

              · public  final  void  wait ( long  timeout )   --给wait一个时间,过了时间后自动唤醒

              · 必须在对odject加锁的同步代码中,在一个线程中,调用object.wait() 时,此线程会释放其拥有的所有锁标记。同时此线程阻塞在object的等待队列中。释放锁,进入等待队列

            · 通知:

              · public final void notify()  --随机唤醒等待队列中一个线程

              . public final void nottifyAll()  --唤醒等待队列的所有的线程

          

package XianCheng.TongXing;

public class BankCard extends Object {
    private int balance;
    //true:表示卡中有钱,false:卡中没有钱
    private boolean flag;
    //存钱功能
    public synchronized void save(int money)  {
        //调用了wait 那么当前线程是否有资源,并且进入等待队列中,等待队列中的线程需要其他线程调用notify来唤醒等待队列的线程,参与下次锁的竞争
        if(flag==true){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        balance=balance+money;
        System.out.println(Thread.currentThread().getName()+"向卡里面充值了 "+money+" 元;卡里余额有:"+balance);
        flag=true;
        //唤醒--等待队列中的线程对象
        this.notify();
    }
    //取钱功能
    public synchronized void take(int money){
        if(flag==false){
            try {
                this.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        balance=balance-money;
        System.out.println(Thread.currentThread().getName()+"向卡里面取出了 "+money+"元"+"卡里面还剩下:"+balance);
        flag=false;
        this.notify();
    }
}

 

package XianCheng.TongXing;

public class Sevetask implements Runnable{
    private  BankCard bankCard;

    public Sevetask(BankCard b) {
        bankCard=b;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankCard.save(1000);
        }
    }
}

 

package XianCheng.TongXing;

public class TakeTask implements Runnable{
    private BankCard bankCard;
    public TakeTask(BankCard b){
        bankCard=b;
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            bankCard.take(1000);
        }
    }
}
package XianCheng.TongXing;

public class Text {
    public static void main(String[] args) {
        BankCard bankCard = new BankCard();
        Sevetask sevetask = new Sevetask(bankCard);
        TakeTask takeTask = new TakeTask(bankCard);
        Thread thread1 = new Thread(sevetask,"充钱者A");
        Thread thread2 = new Thread(takeTask,"充钱者B");
        thread1.start();
        thread2.start();
    }
}

执行结果:

      

    7、线程状态

            NEW:新建状态
            RUNNABLE: start()就绪状态-时间片-运行状态. 统称为RUNNABLE
            BLOCKED: 堵塞状态。加锁时就如该状态
            WAITING: 无期等待:  调用wait方法时会进入该状态
            TIMED_WAITING: 有期等待---当调用sleep方法时就会进入该状态
            TERMINATED: 终止状态。线程的任务代码执行完毕或出现异常。

          线程的状态之间可以通过调用相应的方法,进行转换。

 

    8、线程池

            · 问题:          

              · 线程是宝贵的内存资源、单个线程约占1MB空间,过多分配易造成内存溢出。

              · 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成程序性能下降。

            · 线程池 :

              · 线程容器,可设定线程分配的数量上限。

              · 将预先创建的线程对象存入池中,并重用线程池中的线程对象

              · 避免频繁的创建和销毁。 

                 Executor: 它是线程池的根接口:
                        void execute(Runnable command):执行Runnable类型的任务。
                      ExecutorService: 它是Executor的子接口。---
                       void shutdown():关闭线程池。需要等任务执行完毕。
                       shutdownNow(); 立即关闭线程池。 不在接受新的任务。
                       isShutdown(): 判断是否执行了关闭。
                       isTerminated(): 判断线程池是否终止。表示线程池中的任务都执行完毕,并且线程池关闭了
                       submit(Callable<T> task);提交任务,可以提交Callable
                       submit(Runnable task): 提交任务,可以提交Runnable任务

                    Executors: 它是线程池的工具类,该类提供了创建线程池的一些静态方法

package XianCheng.XianChengChi;

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

public class ConCurrent {
    public static void main(String[] args) {
        //1、创建一个固定长度的线程池
        /* ExecutorService executorService = Executors.newFixedThreadPool(5);*/
        //2、单一线程池
        /*ExecutorService executorService = Executors.newSingleThreadExecutor();*/
        //3、可变线程池 -- 缓存线程池
        /*ExecutorService executorService = Executors.newCachedThreadPool();*/
        //4、延迟线程池
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5);
        /* for(int i=0;i<30;i++){
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"------");
                }
            });
        }*/
        executorService.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"------");
            }
        },5, TimeUnit.SECONDS);
        executorService.shutdown();
    }
}

    阿里建议使用原生的模式创建线程池 :

         int corePoolSize,  核心线程的个数
                              int maximumPoolSize,  最多的线程个数
                              long keepAliveTime,  线程空闲时间。
                              TimeUnit unit,  空闲的单位

      

 

posted @ 2023-08-14 20:55  PHOEDE  阅读(28)  评论(0)    收藏  举报