十、Java多线程和设计模式

一、多线程概述

1、进程: 正在运行的程序,是系统进行资源分配和调用的独立单位。 每一个进程都有它自己的内存空间和系统资源。

2、线程: 是进程中的单个顺序控制流,是一条执行路径 一个进程如果只有一条执行路径,则称为单线程程序。 一个进程如果有多条执行路径,则称为多线程程序。

3、Java程序运行原理 java 命令会启动 java 虚拟机,启动 JVM,等于启动了一个应用程序,也就是启动了一个进程。该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法。所以 main方法运行在主线程中。在此之前的所有程序都是单线程的。

4、jvm虚拟机的启动是多线程的。  主线程与垃圾回收线程

二、多线程实现

java提供了一个类描述线程 Thread,一个JVM中可以创建多个线程同时执行,每个线程都有优先权
java中创建线程对象的几种方式:


1、自己写一个A类继承Thread类,重写run方法,这个A类就叫线程类
注意:
1、run方法就是一个线程对象要做的事情
2、线程对象不能靠调用run方法来启动线程,只能算普通的对象调用了一个普通的方法,和线程没关系了。
3、要想启动一个线程,应该调用start()方法来进行启动,JVM进程中会为这个线程开辟一个对象,这个线程内部调用对应的run方法
4、调用完start()方法之后,线程只是具备了执行的资格,但是还没有真正的执行,只有抢到了CPU执行权的时候,才会执行(执行run方法里面的逻辑,run方法执行完了,线程也就结束了)

Thread类构造方法:
Thread() 分配一个新的 Thread对象。
Thread(String name) 分配一个新的 Thread对象。

Thread类中的成员方法:
public final String getName() 获取当前线程的名字
public final void setName(String name) 将此线程的名称更改为等于参数name 。
public static Thread currentThread() 获取当前方法所在的线程 主线程的名字叫做main
复制代码
package com.shujia.day15;
class MyThread1 extends Thread {
    MyThread1(){
        super();
    }

    MyThread1(String name){
        super(name);
    }

    @Override
    public void run() {
        for (int i = 1; i <= 200; i++) {
            System.out.println(getName()+" -- "+i);
//            System.out.println("--------------------------"+currentThread().getName()+"--------------------------");
        }
    }
}

public class ThreadDemo1 {
    public static void main(String[] args) {
//        MyThread1 t1 = new MyThread1(); // 在使用无参构造方法的时候,给创建的对象起了名字
//        t1.setName("AAA");
//        MyThread1 t2 = new MyThread1();
//        t2.setName("BBB");
//        //启动线程
////        t1.run();
////        t2.run();
//        t1.start();
//        t2.start();

        //使用带参数的构造方法,在创建线程对象的时候给线程起名字
        MyThread1 t1 = new MyThread1("AAA");
        MyThread1 t2 = new MyThread1("BBB");
        t1.start();
        t2.start();

        System.out.println("--------------------------"+Thread.currentThread().getName()+"--------------------------");
    }
}
复制代码

2、自己写一个A类实现Runnable接口,实现run方法,这个A类就叫线程类

             实现接口方式的好处:可以避免由于Java单继承带来的局限性。 适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码,数据有效分离,较好的体现了面向对象的设计思想。

 
复制代码
package com.shujia.day15;

/*
    第二种实现线程方式:自己写一个A类实现Runnable接口,实现run方法,这个A类就叫线程类,需要借助Thread类来创建线程对象
 */
class MyRunnable implements Runnable{
    //
    @Override
    public void run() { // 线程要执行的逻辑在run方法中编写
        for (int i = 1; i <= 200; i++) {
            //因为第二种是实现了Runnable接口,接口中只有一个抽象的run方法,找不到getName()方法,所以报错
            //如果想要在这里输出线程的名字的话,可以间接地获取当前线程对象,然后再获取线程名字
            System.out.println(Thread.currentThread().getName()+" -- "+i);
        }
    }
}

public class MyRunnableDemo1 {
    public static void main(String[] args) {
        //如何创建线程对象并启动呢?
        MyRunnable myRunnable = new MyRunnable();

        //需要借助Thread类来创建线程对象 public Thread(Runnable target)
//        Thread t1 = new Thread(myRunnable);
//        Thread t2 = new Thread(myRunnable);
//        Thread t3 = new Thread(myRunnable);
//        t1.setName("大哥");
//        t2.setName("二哥");
//        t3.setName("三弟");

        //public Thread(Runnable target, String name) 创建线程对象的同时给线程起名字
        Thread t1 = new Thread(myRunnable, "大哥");
        Thread t2 = new Thread(myRunnable, "二哥");
        Thread t3 = new Thread(myRunnable, "三第");

        //启动线程
        t1.start();
        t2.start();
        t3.start();

    }
}
复制代码

 



3、自己写一个A类实现Callable接口,实现call方法,这个A类就叫线程类,需要结合线程池才能使用

                好处: 可以有返回值 可以抛出异常 弊端: 代码比较复杂,所以一般不用

三、线程的调度

1、线程有两种调度模型:

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

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

Java使用的是抢占式调度模型。

2、线程的优先级

每个线程都有优先级。

默认情况下,线程的优先级都是5,优先让优先级高的线程使用CPU

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

public final void setPriority(int newPriority) 修改线程的优先级 范围是[1,10]

注意:优先级高的线程只是说先执行的概率会大一些,并不是一定会先执行。

四、线程控制

线程休眠:public static void sleep(long millis)  毫秒

线程加入: public final void join()   其他线程会等待该线程执行结束

线程礼让 public static void yield() 

后台线程 public final void setDaemon(boolean on)

后台线程(守护线程):
线程分为两种:
用户线程:是没有设置Daemon的线程
守护线程:当没有用户线程的时候守护线程自动死亡

中断线程 public final void stop()

     public void interrupt()

                             线程的生命周期图

 

五、解决线程安全问题

首先想为什么出现问题?(也是我们判断是否有问题的标准)

是否是多线程环境

是否有共享数据

是否有多条语句操作共享数据

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

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

具体实现:

①:同步代码块
      
格式: synchronized(对象){需要同步的代码;}

  同步的前提

    多个线程

    多个线程使用的是同一个锁对象

  同步的好处: 同步的出现解决了多线程的安全问题。

    同步的弊端 :当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。

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

    如果锁对象是this,就可以考虑使用同步方法。 否则能使用同步代码块的尽量使用同步代码块。

 解决线程安全问题的第二种方案:使用Lock锁来实现

但是通过观察API后发现,Lock本身是一个接口,所以找一个实现类:ReentrantLock

    void lock() 加锁
void unlock() 释放锁

死锁的问题:

同步弊端 效率低 如果出现了同步嵌套,就容易产生死锁问题

死锁问题及其代码 是指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象 同步代码块的嵌套案例


六、线程中的通信 
代码改进:
A:通过等待唤醒机制实现数据依次出现
         B:把同步代码块改进为同步方法实现
                              线程的状态转换图

    

 

七、线程组
概述:
Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
    默认情况下,所有的线程都属于主线程组。
     public final ThreadGroup getThreadGroup()
    我们也可以给线程设置分组
    Thread(ThreadGroup group, Runnable target, String name)
    
直接对组进行操作,也会影响到组中的线程,有了线程组,方便管理和分类
 
八、线程池 Executors
概述:程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。

线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。 在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池

线程池的分类:
tatic ExecutorService newCachedThreadPool() 创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。
static ExecutorService newSingleThreadExecutor() 创建一个使用从无界队列运行的单个工作线程的执行程序。
static ExecutorService newFixedThreadPool(int nThreads) 创建一个线程池,该线程池重用固定数量的从共享无界队列中运行的线程。

注意:
线程一旦被放入了线程池中就会开始执行(相当于调用了start()方法,具备了执行资格);
线程池中的线程之间会互相抢CPU执行权;
线程池需要手动关闭,一般情况下需要不断地放入线程执行,一般不需要关。

 九、定时器   
概述:
定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能
构造方法:Timer() 创建一个新的计时器。
方法:
void schedule(TimerTask task, long delay)  在指定的延迟之后安排指定的任务执行。  
void schedule(TimerTask task, long delay, long period) 在指定的延迟之后开始 ,重新执行固定延迟执行的指定任务。

十、设计模式 
概述:
设计模式:是历代程序员开发总结得出的经验,并被后人持续的使用
分类:
创建型模式
简单工厂模式:
              简单工厂模式(一个工厂中可以造很多的各种各样的对象)
              这个模式的缺陷是如果有新的对象的话,需要修改很多类,而一般情况下,工厂类不轻易修改
        工厂方法模式:每一个对象都由自身的一个工厂创建出来
        单例模式:指的是在程序运行过程中,类内存中有此仅有一个对象
            懒汉式
饿汉式
        面试的时候说懒汉式,开发的时候使用饿汉式。因为懒汉式有可能存在线程安全的问题。懒汉式写的时候需要加上synchronized关键字
    行为型模式
结构型模式
 简单工厂模式(一个工厂中可以造很多的各种各样的对象)
这个模式的缺陷是如果有新的对象的话,需要修改很多类,而一般情况下,工厂类不轻易修改


 
    
 

  
 
 
 
 
posted @   SIKeborn  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示