多线程编程和创建多线程的方式

一、多线程编程

多线程编程是一种系统编程,实现多任务多用户操作

多任务指的是:多进程编程、多线程编程、协程编程

进程(Process):操作系统的基本单位,可以直接申请内存空间,独立的,不影响其他进程,特点是稳定,效率较低

线程(Thread):依赖于进程,可以独立运行,更轻量,会影响其他线程,从而导致整个进程崩溃,特点是不稳定,效率较高

协程(Coroutime):是轻量级的线程

发展历史从B语言-->C语言(高级语言之父)

Unix操作系统:所有操作系统之父,第一款多任务多用户操作系统

注意:在多核CPU诞生之前,所有的多任务操作都是伪多任务!!

并发:指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行。即多个任务抢占少量资源,交替执行不同任务的方式就是并发(大量用户抢占少量的资源)。

并行:指在同一时刻,有多条指令在多个处理器上同时执行,所以无论从微观还是从宏观来看,二者都是一起执行的

并发和并行的相同点:
          并发和并行的目标都是最大化CPU的使用率,将cpu的性能充分压榨出来。

并发和并行的不同点:
        (1)并行在多处理器系统中存在,而并发可以在单处理器和多处理器系统中都存在

        (2)并行要求程序能够同时执行多个操作,而并发只是要求程序“看着像是”同时执行多个操作,其实是交替执行。

时间片:每一个任务每次在CPU中占有的时间

  • Thread.currentThread()获取当前线程对象
  • Thread.currentThread().getName();获取当前线程对象的名

优先级别时间调度:开机、网络是基层,后才能运行其他需要开机联网才能运行的软件

注意:操作系统使用底层的多任务使用的就是时间片轮换机制,配合优先级别调度

5 1 4 3 2

优先级别越高越先执行,5先执行,执行后减1,有两个4,他俩互相抢

二、多进程编程

多线程编程就是抢占资源

Java自身提供了四种创建多线程的方案:

1.继承Thread

2.实现Runnable接口

3.实现Callable接口

4.线程池实现多线程

(1)继承Thread类:因为Thread类描述线程事物,具备线程应该有功能。
Thread常用方法
start()线程编程的启动方法
getId()获取进程的ID
getName()获取当前进程的名称
getPriority()优先级别
getState()线程的状态(创建、就绪、运行、等待、销毁)
setDamemo()将当前线程设置成守护线程
interrupt()线程抛出一个中断信号
isAlive()判断线程是否活着
join()使得子线程运行完后主线程才开始运行(阻塞当前线程)
sleep()让线程停几秒钟再运行(进入休眠状态)
currentThread.setName("主函数名称")通过构造函数传参给线程起名字(子线程)
yield()表示(暗示hint,不一定放弃)当前线程放弃执行一次
/**
 * 通过继承Thread类实现多线程编程,重写run()方法,
 * 调用start()方法才能实现抢占资源
 */
public class MyThread extends Thread {
    public static void main(String[] args) {

        MyThread myThread = new MyThread();

        myThread.start();

        for (int i = 0; i < 10000; i++) {
            System.out.println("主线程运行了----" + i + "次");
        }
    }

    @Override
    public void run(){
        for (int i = 0; i < 10000; i++) {
            System.out.println("一个子线程运行了-----" + i + "次");
        }


    }
}

那为什么不直接创建Thread类的对象呢?

Thread t1 = new Thread(); t1.start();//这样做没有错

        但是该start调用的是Thread类中的run方法,而这个run方法没有做什么事 情,更重要的是这个run方法中并没有定义我们需要让线程执行的代码。

创建线程的目的是什么?

         是为了建立单独的执行路径,让多部分代码实现同时执行。也就是说线程创建并执行需要给定的代 码(线程的任务)。对于之前所讲的主线程,它的任务定义在main函数中。自定义线程需要执行的任务都 定义在run方法中。

Thread类中的run方法内部的任务并不是我们所需要,只有重写这个run方法,既然Thread类已经定义了线程任务的位置,只要在位置中定义任务代码即可。所以进行了重写run方法动作。 

Thread.currentThread().getName()//获取线程的名字

(2)实现Callable接口(接口式编程)

使用Callable接口实现的多线程对象,最后能够得到线程方法返回的结果

FutureTask(V) implements RunnableFuture

RunnableFuture  ectends  Runnable,Future

说明FutureTask(V)是RunnableFuture的子类

RunnableFuture是Runnable,Future的子类

总的来说:FutureTask(V)是Runnable的子类,

new Thread(futuretask).start();

代码示例:

public class MyThread3 implements Callable {
    public static void main(String[] args) {
        MyThread3 mt = new MyThread3();
        FutureTask futureTask = new FutureTask(mt);
        new Thread(futureTask).start();

        try {
            Integer result = (Integer) futureTask.get();
            System.out.println("线程方法返回的结果" + result);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        for (int i = 0; i < 10000; i++) {
            System.out.println(Thread.currentThread().getName() + "主线程运行了----" + i + "次");
        }


    }


    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 10000; i++) {
            sum += i;
            System.out.println(Thread.currentThread().getName() + "一个子线程运行了-----" + i + "次");
        }
        System.out.println(sum);
        return sum;
    }
}

继承Thread和Runnable的区别:

  • 继承Thread的线程对象之间不能共享数据
  • 实现Runnable线程对象,如果使用不同的Thread对象启动同一个Runnable线程对象,则会共享数据,这种现象会导致线程安全问题(又可能)
(3)实现 Runnable接口创建线程

        创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方 法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。 怎么还要实现Runnable接口,Runable是啥玩意呢?Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。 类必须定义一个称为 run 的无参数方法。

总结: 创建线程的第二种方式:实现Runnable接口。

1. 定义类实现Runnable接口。

2. 覆盖接口中的run方法。

3. 创建Thread类的对象。

4. 将Runnable接口的子类对象作为参数传递给Thread类的构造函数。

5. 调用Thread类的start方法开启线程。

代码示例:

/**
 * 通过实现Runnable接口,重写run()方法,因为Thread实现了Runnable接口,
 * 所以可以通过创建Thread调用start()方法实现抢占资源的多线程
 */
public class MyThread2 implements Runnable {

    public static void main(String[] args) {
        MyThread2 myThread1 = new MyThread2();
        MyThread2 myThread2 = new MyThread2();

        new Thread(myThread1).start();
        new Thread(myThread2).start();

        for (int i = 0; i < 10000; i++) {
            System.out.println("主线程运行了----" + i + "次");

        }
    }
    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            System.out.println("一个子线程运行了-----" + i + "次");
        }
    }
}

三、线程安全问题线程安全问题本质上就是错误数据变化问题

(1)问题产生的原因

1. 线程任务中在操作共享的数据。

2. 线程任务操作共享数据的代码有多条(运算有多个)。

(2)如何解决线程安全问题

        通过加锁解决

同步锁:synchronized 关键字

该锁有三种使用方式:

1.同步块

2.将方法加锁,加到方法上,方法就是锁

3.将静态方法加锁,当前静态方法所处的类就是锁

锁加的范围越小效率越高!!!

代码示例:

/**
 * 解决线程安全问题的方法就是加锁
 * 1.给代码块加锁
 * 2.给方法加锁
 * 3.给静态方法加锁,静态方法所在的这个类的字节码文件
 *
 * synchronized关键字就是同步锁
 * 该锁必须使用一个对象作为钥匙
 */
public class MyThread5 extends Thread {


    private static int count;
    //创建一个对象
//    Object obj = new Object();

    @Override
    public  void run() {
        test();
    }

    public synchronized static void test(){
        for (int i = 0; i < 10000; i++) {
            //++ --是非原子性代码
            //这个锁是个对象,所以需要创建一个对象放进括号中
            synchronized (MyThread5.class){
                count++;
            }
        }
        System.out.println(Thread.currentThread().getName() + "运行了"+ "count=" + count);
    }

    //两个线程同时共享一个进程,出现线程安全问题,即错误的数据问题,两个线程进行抢占的过程中将数据搞错啦
    public static void main(String[] args) {
        MyThread4 myThread = new MyThread4();
        new Thread(myThread).start();
        new Thread(myThread).start();
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

simpleHan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值