多线程,线程安全,高并发及线程安全,并发包,线程池方式

一、多线程

1、并发与并行

并行:指两个或多个事件在同一时刻发生(同时执行)。
并发:指两个或多个事件在同一个时间段内发生(交替执行)。
并发于并行

2、线程与进程

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

线程:是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至少有一条线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
——线程是进程的可执行单元一个
——进程可以有多条线程
——每个线程执行都会有独立的内存空间

java只有单进程,然后有多线程
一个进程一次只能执行一条线程,所以java中只有多线程并发,没有多线程并行
线程的调度:
——分时调度:所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间
——抢占式调度:优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性)
——Java线程的调度方式: 抢占式

3、Thread类

概述:
——java.lang.Thread类代表线程,所有的线程对象都必须是Thread类或其子类的实例   每个线程的作用是完成一定的任务,实际上就是执行一段程序流即一段顺序执行的代码
——Java使用线程执行体来代表这段程序流,在Tread线程中,使用run()方法代表线程执行体构造方法

     public Thread():创建一个新的线程对象,默认名称
     public Thread(String name):创建一个指定名字的新的线程对象
     public Thread(Runnable target):创建一个带有指定任务的线程对象,通过参数Runnable指定任务
     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() :返回对当前正在执行的线程对象的引用
     通过Thread类的api,可以指定创建线程有2种方式:
     —— 1.通过继承Thread类的方式
     —— 2.通过实现Runnable接口的方式

4、继承方式创建线程

步骤:
——创建一个子类继承Thread类在
——子类中重写run方法,把线程需要执行的任务代码放入run方法中
——创建子类对象,调用start()方法启动线程,执行任务
实现:

public class MyThread extends Thread{
    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("子线程i循环:第"+i+"次循环");
        }
        System.out.println("子线程任务结束了...");
    }
}

public class Test {
    public static void main(String[] args) {
        /*
            注意:
                1.每个java程序都有一条主线程,主线程中会调用main方法
                2.线程不能重复启动,只能启动一次
                3.线程执行完任务就会销毁
                4.主线程必须等所有的子线程都执行完毕才能销毁
                5.启动线程一定要调用start方法,不要直接调用run方法
            通过继承方式创建线程:
                1.创建子类继承Thread类,重写run方法
                2.在run方法中书写线程需要执行的任务代码
                3.创建子类线程对象
                4.调用start方法启动线程,执行任务
         */
        // 1.创建线程子类对象
        MyThread mt = new MyThread();
        // 2.启动线程,执行任务
        mt.start();

        for (int j = 0; j < 100; j++) {
            System.out.println("主线程j循环:第"+j+"次循环");
        }

    }
}

5、实现方式创建线程

Runnable是一个任务接口,里面有一个抽象方法run(),可以在run方法中书写线程的任务代码
实现步骤:
——创建实现类实现Runnable接口
——在实现类中,重写run方法,把线程需要执行的任务代码放入run方法中创建实现类对象
——创建Thread线程对象,并传入实现类对象
——使用Thread线程对象调用start方法启动线程,执行任务
实现:

public class Test {
    public static void main(String[] args) {
        /*
            通过实现Runnable方式:
                1.创建实现类实现Runnable接口,重写run方法
                2.把线程需要执行的任务代码放入run方法中
                3.创建Thread线程对象,并传入Runnable的实现类对象
                4.调用start方法启动线程执行任务
         */
        // 1.创建实现类对象
        MyRunnable mr = new MyRunnable();

        // 2.创建Thread线程对象,传入Runnable的实现类对象
        Thread t = new Thread(mr);

        // 3.调用start方法启动线程执行任务
        t.start();

        // 主线程的任务代码
        for (int j = 0; j < 100; j++) {
            System.out.println("主线程j循环:第"+j+"次循环");
        }

    }
}

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("子线程i循环:第"+i+"次循环");
        }
    }
}

6、匿名内部类方式

原理: 可以传入Runnable接口的匿名内部类
步骤:
——创建Thread线程对象,并传入Runnable接口的匿名内部类
——在Runnable匿名内部类中重写run方法,书写线程需要执行的任务代码
——使用Thread线程对象调用start方法启动线程,执行任务
实现:

public class Test {
    public static void main(String[] args) {
        /*
            通过Runnable接口的匿名内部类方式:
                1.创建Thread线程对象,并传入Runnable接口的匿名内部类
                2.在匿名内部类中重写run方法,把线程需要执行的任务代码放在run方法中
                3.调用start方法启动线程执行任务
         */
        // 1.创建并启动线程
        Thread t = new Thread(new Runnable() {
            @Override
            public void run() {
                // 线程任务代码
                for (int i = 0; i < 100; i++) {
                    System.out.println("子线程的i循环:第" + i + "次循环");
                }
            }
        });
        t.start();

        // 2.主线程任务代码
        for (int j = 0; j < 100; j++) {
            System.out.println("主线程的j循环:第" + j + "次循环");
        }

    }
}

7、创建并启动多条线程

通过继承的方式:

public class Test {
    public static void main(String[] args) {
        // 需求: 创建并启动4条子线程
        // 创建4个线程对象
        MyThread mt1 = new MyThread("线程1");
        MyThread mt2 = new MyThread("线程2");
        MyThread mt3 = new MyThread("线程3");
        MyThread mt4 = new MyThread("线程4");
        // 启动线程执行任务
        mt1.start();
        mt2.start();
        mt3.start();
        mt4.start();
    }
}

public class MyThread extends Thread {

    public MyThread() {
    }

    public MyThread(String name) {
        super(name);
    }

    @Override
    public void run() {
        // 线程任务代码
        System.out.println(getName() + ":子线程的任务代码...");
    }
}

通过实现的方法:

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        // 线程任务代码
        System.out.println(Thread.currentThread().getName() + ":子线程的任务代码...");
    }
}

public class Test {
    public static void main(String[] args) {
        // 需求: 创建并启动4条子线程
        // 1.创建Runnable实现类对象
        MyRunnable mr = new MyRunnable();

        // 2.创建Thread线程对象,并传入Runnable实现类对象
        Thread t1 = new Thread(mr,"线程1");
        Thread t2 = new Thread(mr,"线程2");
        Thread t3 = new Thread(mr,"线程3");
        Thread t4 = new Thread(mr,"线程4");

        // 3.调用start方法启动线程,执行任务
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}

8、实现方式创建线程的优势

实现Runnable接口比继承Thread类所具有的优势:

  1. 适合多个相同的程序代码的线程去共享同一个资源(任务)。
  2. 可以避免java中的单继承的局限性。
  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
  4. 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。

二、线程安全

1、线程安全问题

线程安全问题演示:需求:

  1. 使用多线程模拟4个窗口共同卖100张电影票
  2. 分析:
    ——4个窗口---->4条线程
    ——共同卖100张电影票
    ——卖票的代码都是一样的---->4条线程的任务代码是一样的
  3. 实现:
public class MyRunnable implements Runnable {
    // 多条线程共享
    int tickets = 100;

    @Override
    public void run() {
        // 线程任务代码--->卖票
        // 循环卖票
        while (true) {
            // 判断--出口
            if (tickets < 1){
                break;
            }

            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println(Thread.currentThread().getName() + ":正在出售第" + tickets + "张票");
            tickets--;
        }
    }
}


public class Test {
    public static void main(String[] args) {
        // 需求: 使用多线程模拟4个窗口共同卖100张票
        // 创建任务对象
        MyRunnable mr = new MyRunnable();
        // 创建4条线程并启动
        new Thread(mr,"窗口1").start();
        new Thread(mr,"窗口2").start();
        new Thread(mr,"窗口3").start();
        new Thread(mr,"窗口4").start();
        /*
            问题:
                1.重复票
                2.遗漏票
                3.负数票
         */
    }
}
  1. 原因: 线程的调度是抢占式
    ——当某条线程在执行卖票的代码的时候,被其他线程干扰了,导致程序运行结果受影响
  2. 解决:
    ——当某条线程在执行卖票的代码的时候,不要被其他线程干扰了---->加锁
    ——Synchronized—>同步代码块,同步方法
    ——Lock锁

2、synchronized

synchronized关键字:表示“同步”的。它可以对“多行代码”进行“同步”
——将多行代码当成是一个完整的整体,一个线程如果进入到这个代码块中,会全部执行完毕,执行结束后,其它线程才会执行。
这样可以保证这多行的代码作为完整的整体,被一个线程完整的执行完毕。

synchronized被称为“重量级的锁”方式,也是“悲观锁”——效率比较低。

synchronized有几种使用方式:
a).同步代码块
b).同步方法【

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值