JavaSE多线程

一:程序、进程和线程

1.1:程序概述

静态代码

例如 软件

1.2:进程概述

动态过程

如 运行中的qq等软件

    • 一个正在运行的程序

1.3进程与程序联系与区别:

  • 程序是静态的,进程是动态的;

1.4:线程概述

简而言之:线程是进程的一个执行单元。比进程更小的独立运行的基本单位。

  • 注:一个程序至少一个进程,一个进程至少一个线程。比如视频中同时有声音、图像、弹幕等;
  • 简单
    • 进程
      • 线程;任务

二:线程的创建和启动(Java如何实现线程的)

  • 概述
    • Java语言的JVM允许程序运行多个线程(Java允许同时执行多个代码的),它通过java.lang.Thread类来体现。
      • Thread类的特性:
        • 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体。
        • 通过该Thread对象的start()方法来启动这个线程,而非直接调用run()。
    • JDK1.5之前创建新执行线程有两种方法:
      • 继承Thread类的方式;
      • 实现Runnable接口的方式;

2.1方式一

  • 继承Thread类
  1. 定义子类继承Thread类。
  2. 子类中重写Thread类中的run方法,把新线程要做的事写在run方法中。
  3. 创建Thread子类对象,即创建线程对象。
  4. 调用线程对象start方法:启动线程,调用run方法。
  • 代码实例:主线程和分支线程并发执行:

       

package 多线程概述01;

public class Demo {
    /*
    * 1:新建一个类,继承Thread
    * 2:重写run方法
    * 3:创建一个实例
    * 4:调用start
    *
    * */
    public static void main(String[] args) {
        Thread t1 =  new MyThread();
        t1.start();
    }
}
// 1、定义子类继承Thread类。
package 多线程概述01;
//线程干嘛的?干活的,执行任务
public class MyThread extends Thread{
    //以后你想用线程做什么任务,任务代码就写进run方法
    @Override
    public void run() {
        for (int i = 1;i<101;i++){
            System.out.println("mythread:"+i);
        }
    }
}
    

2.2.0知识点补充

  • 并发
    • 不同的任务,在同一时刻不同时间点执行。并发现象
  • 并行
    • 在相同的时间,做不同的任务

2.2方式2

  • 实现Runnable接口
    • Thread(Runnable target, String name):创建新的Thread对象;
  1. 定义子类,实现Runnable接口。
  2. 子类中重写Runnable接口中的run方法,把新线程要做的事写在run方法中
  3. 创建自定义的Runnable的子类对象
  4. 通过Thread类含参构造器创建线程对象,将Runnable接口的子类对象作为实际参数传递给Thread 类的构造器中。
  5. 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
    • 代码示例
package 多线程实现方式二;

public class Demo {
    public static void main(String[] args) {
        /*
        * 1:自定义一个类,实现Runable
        * 2:重写run
        * 3:创建一个对象
        * 4:再创建一个Thread,把刚刚创建好的对象传递进去
        * 5:调用statrt方法
        * */
        MyRunnable myRunnable =   new MyRunnable();
         Thread t1 =  new Thread(myRunnable);
         t1.start();
    }
}

package 多线程实现方式二;

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        System.out.println(111);
    }
}

2.3两种创建方式的对比

  • 总结
    • Thread类,可以直接使用父类的方法,有单继承的局限性
    • Runnable,拓展性好

三:线程的状态

3.1:Java多线程五种基本状态

  1. 新建状态(New)
  2. 就绪状态Runable: 新建状态的线程对象被start()后
  3. 运行状态(Running):run方法抢占到CPU的执行权
  4. 阻塞状态:
    1. 等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
    2. 同步阻塞:线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
    3. 其他阻塞 :通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
  1. 死亡状态(Dead):结束

四:线程控制和线程安全

4.1:线程控制

  • 获取当前线程、名称和设置名称
  • 方法

0、获取当前线程 static Thread currentThread() 1、获取名称 通过 getName()方法获取线程对象的名字 2、设置名称 通过构造函数可以传入String类型的名字 Thread(String name) 分配新的 Thread 对象。name - 新线程的名称。 setName(String name) 改变线程名称,使之与参数 name 相同。

  • 获取当前线程、成员方法实现设置线程名称
  • 构造函数实现设置线程名称

4.1.1休眠线程

sleep —— 让当前的正在执行的线程暂停指定的时间,并进入阻塞状态。在其睡眠的时间段内,该线程

由于不是处于就绪状态,因此不会得到执行的机会。即使此时系统中没有任何其他可执行的线程,处于

sleep()中的线程也不会执行。因此sleep()方法常用来暂停线程执行。

Thread.sleep(1000);

4.1.2守护线程

当进程中不存在非守护线程了,则守护线程自动销毁。

t1.setDaemon(true);

4.1.3加入线程

join(), 当前线程暂停, 等待指定的线程执行结束后, 当前线程再继续;

t1.join();

4.1.4礼让线程

static void yield() 暂停当前正在执行的线程对象,并执行其他线程

Thread.yield(); // 让出CPU

4.1.5设置线程优先级

setPriority(int newPriority) 更改线程的优先级。

getPriority() 返回线程的优先级

System.out.println(t1.getPriority()); System.out.println(t2.getPriority()); t1.setPriority(1); t1.start(); t2.setPriority(10); t2.start();

4.1.7:线程等待wait

    • 一直在阻塞状态
    • notify:唤醒另一个
    • notifyAll:唤醒所有

4.2:数据共享

线程数据共享,很容器造成线程不安全

4.3线程同步

  • Java中提供了线程同步的机制,帮我们解决线程安全问题
    • 同步代码块
    • 同步方法

4.3.1:同步代码块

synchronized (lock){//():中的参数指的是给一个锁? 任意对象

4.3.2同步方法

  • synchronized关键字加到方法声明上

五:线程的并发与协作

5.1:线程间的通信

  • 什么是线程间的通信
    • 线程有一定规律的交替执行

5.2:两个线程间的通信实现

package 两个线程间的通信01;

public class Demo {

    public static void main(String[] args) { //[main,5,main]
        Print print = new Print();
        new Thread(){

            @Override
            public void run() {
               while (true){
                   print.m1();
               }
            }
        }.start();
        new Thread(){

            @Override
            public void run() {
               while (true){
                   print.m2();
               }
            }
        }.start();
    }
}
class Print{
    private int flag = 1;
    public  void m1(){
       synchronized (this){
           if (flag!=1){
               try {
                   this.wait();
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
           System.out.print("坏");
           System.out.print("蛋");
           System.out.print("吃");
           System.out.print("piao");
           System.out.println();
           flag = 2;
           this.notify();

       }
    }
    public  void m2(){
       synchronized (this){
           if (flag!=2){
               try {
                   this.wait();
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
           }
           System.out.print("好");
           System.out.print("蛋");
           System.out.print("真");
           System.out.print("牛");
           System.out.println();
           flag = 1;
           this.notify();
       }
    }

}

/*
class MyRunnable implements Runnable{

    @Override
    public void run() {
        while (true){
            if (Demo.a!=2){
                try {
                   Demo.o.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            m();
            Demo.a = 1;
           Demo.o.notify();
        }
    }
    public synchronized void m(){
        System.out.print("坏");
        System.out.print("蛋");
        System.out.print("吃");
        System.out.print("piao");
        System.out.println();
    }
}
class MyRunnable2 implements Runnable{

    @Override
    public void run() {
        while (true){
            if (Demo.a!=1){
                try {
                    Demo.o.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            m();
            Demo.a = 2;
            Demo.o.notify();
        }
    }
    public synchronized   void m(){
        System.out.print("好");
        System.out.print("蛋");
        System.out.print("真");
        System.out.print("牛");
        System.out.println();
    }
}*/

5.3:三个线程之间的通信

5.4:生产者、消费者

  • 缓冲区是实现并发的核心,缓冲区的设置有3个好处:
  1. 实现线程的并发协作
  2. 解耦了生产者和消费者
  3. 解决忙闲不均,提高效率
  4. 生产者消费者模式
package 生产者和消费者03;

public class Demo {

    public static void main(String[] args) {
        DataBuffer dataBuffer = new DataBuffer();
        Shengchanzhe scz = new Shengchanzhe(dataBuffer);
        XiaoFeiZhe xfz = new XiaoFeiZhe(dataBuffer);
        scz.start();
        xfz.start();
    }

}
class Data{

   public int  data;
    //给数据赋值,要多少个数据
    public Data(int data) {
        this.data = data;
    }

    public Data() {
    }
}
//缓存区
class DataBuffer{
     //数组的索引
     private int index;
    //存数据
    Data[] array = new Data[10];

    //存
    public synchronized void push(Data data){
        //存数据
               //数组满了
        while (index == array.length){
            //数组满了,让生产数据的线程,先听一下
            try {
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //没进入循环
        array[index++] =data;
        notify();


    }

    //取

    public synchronized Data pop(){
        while (index == 0){
            try {
                wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        notify();
        index--;
        return array[index];//我们正在从缓存区索取数据


    }


}
class Shengchanzhe extends Thread{

    private DataBuffer dataBuffer;
    public Shengchanzhe(){}
    public Shengchanzhe(DataBuffer dataBuffer) {
        this.dataBuffer = dataBuffer;
    }
     //生产数据
    @Override
    public void run() {
        for (int i = 0;i<10;i++){
            System.out.println("我正在生产数据");
            //该方法是在生产数据,用数据类(Data)来封装数据
            Data data = new Data(i);
            //放缓存区
            dataBuffer.push(data);

        }
    }
}
class XiaoFeiZhe extends Thread{

    private DataBuffer dataBuffer;

    public XiaoFeiZhe(DataBuffer dataBuffer) {
        this.dataBuffer = dataBuffer;
    }

    @Override
    public void run() {
        for (int i = 0;i<10;i++){

            Data pop = dataBuffer.pop();
            System.out.println("消费者"+pop.data);

        }
    }
}

六:线程池、组

6.1:使用Callable接口创建线程

  • 使用Callable接口创建线程是JDK5.0新增的;
  • 与使用Runnable相比, Callable功能更强大些:
    • 相比run()方法,可以有返回值
    • 方法可以抛出异常;
    • 支持泛型的返回值;
    • 需要借助FutureTask类,比如获取返回结果;
  • 具体是创建Callable接口的实现类,并实现call()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。
  • 看着好像有点复杂,直接来看一个例子就清晰了:
public class MyCallble implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println(1111);

        int sum = 0;
        for (int i = 1;i<101;i++){
            sum+=i;
        }
        return  sum;
    }
}

6.2:线程组概述

  • 数据的安全
    • 同一个组,数据可以互相访问的
    • 不是同一个,数据不可以互相访问

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

        //获取main线程是属于哪个组---》Thread--->getThreadGroup()
        Thread tmain = Thread.currentThread();
        //获取线程组
        ThreadGroup threadGroup = tmain.getThreadGroup();
        //获取该线程组的名字
        System.out.println(threadGroup.getName());
        //自定义的线程,没有分组
        Thread thread = new Thread() {
            @Override
            public void run() {

            }
        };
        System.out.println(thread.getThreadGroup().getName());
    }
}
  • 自定义线程组
package 线程池概述14;

import Callble概述13.MyCallble;

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

public class Demo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //线程池类
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //给线程池要线程
       /* executorService.submit(new Runnable() {
            @Override
            public void run() {

                System.out.println("哈哈,我们在学习线程池!");
            }
        });*/
        MyCallble myCallble = new MyCallble();
        Future<Integer> future = executorService.submit(myCallble);
        System.out.println(future.get());
        executorService.shutdown();


    }
}

6.3线程池(工厂)

6.3.1概述

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

package 线程池概述14;

import Callble概述13.MyCallble;

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

public class Demo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //线程池类
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        //给线程池要线程
       /* executorService.submit(new Runnable() {
            @Override
            public void run() {

                System.out.println("哈哈,我们在学习线程池!");
            }
        });*/
        MyCallble myCallble = new MyCallble();
        Future<Integer> future = executorService.submit(myCallble);
        System.out.println(future.get());
        executorService.shutdown();


    }
}

6.3.2优点

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。

6.3.3使用线程池方式--Runnable接口

  • 通常,线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。
  1. Executors:线程池创建工厂类
    1. public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
  2. ExecutorService:线程池类
    1. Future submit(Runnable task):获取线程池中的某一个线程对象,并执行
  3. Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
  4. 使用线程池中线程对象的步骤:
    1. 创建线程池对象
    2. 创建Runnable接口子类对象
    3. 提交Runnable接口子类对象
    4. 关闭线程池

6.3.42.3使用线程池方式—Callable接口

  1. Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
  2. ExecutorService:线程池类
  • Future submit(Callable task):获取线程池中的某一个线程对象,并执行线程中的call()方法
  1. Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
  2. 使用线程池中线程对象的步骤:
    1. 创建线程池对象
    2. 创建Callable接口子类对象
    3. 提交Callable接口子类对象
    4. 关闭线程池
  • 50
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值