Java高级知识复盘之线程创建方式,常用方法,安全问题

Java高级知识复盘之线程创建方式,常用方法,安全问题

线程基础知识-名词
线程创建方式
线程常用方法
线程安全问题

1 技术简介

1.1 进程

(1)一个程序就是一个进程
(2)进程是资源分配的基本单位
	操作系统分配资源
	操作系统:operating system      os
	资源:CPU、内存等
(3)一个进程中包含多个线程  多线程

1.2 线程

(1)真正执行任务的是线程
(2)线程是任务调度的基本单位
(3)多个线程共享进程中的资源

1.3 并发

(1)同时发生
	多个请求同时发生了
(2)高并发:
	大量的同时发生
	双十一
	服务器支持高并发

1.4 并行

(1)同时进行
(2)并行发生前提:
	多核CPU
(3)单核CPU:
	同一时刻,只执行一个任务
(4)案例:
	40个任务	4核
    每核执行10个任务,切换执行

1.5 串行

(1)任务是一个接一个的执行
(2)单核CPU:
	串行化执行多个任务
(3)宏观并行、微观串行

1.6 同步

(1)一个任务的开始要等待另外一个任务的结束  ***
(2)举例:
	接力赛

1.7 异步

(1)一个任务的开始不用等待另外一个任务的结束
(2)举例:
	有道
	网易云   搜索任务|播放任务

2 线程创建(重要)

(1)创建一个类,继承Thread(2)实现Runnable接口
(3)实现Callable接口
(4)线程池

2.1 创建方式一

2.1.1

package com.javasm.thread.create;

/**
 * @author: ShangMa
 * @className: Emp
 * @description: 员工类
 * @date: 2022/8/16 10:14
 */
public class Emp extends Thread {
    /**
     * 创建方式一:
     * 继承Thread类,重写run方法
     * run方法:定义当前线程执行任务
     * 了解
     * 类与类:单继承
     */

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("线程执行任务: " + i);
        }

    }
}

2.1.2

package com.javasm.thread.create;

/**
 * @author: ShangMa
 * @className: CreateExercise
 * @description: 线程创建方式
 * @date: 2022/8/15 18:10
 */
public class CreateExercise {
    public static void main(String[] args) {

    }

    /**
     * 线程创建方式一
     */
    private static void method1() {
        /**
         * 多个线程:
         *  main主线程
         *  自定义线程emp
         */
        System.out.println("main start");
        Emp emp = new Emp();
        /**
         * start方法:
         *  开启
         *
         *  告知os调度器准备好了,可以被调度了
         *  一旦被调度到了,自动调用run方法执行任务
         */
        emp.start();
        System.out.println("main end");
    }
}

2.2 创建方式二

package com.javasm.thread.create;

/**
 * @author: ShangMa
 * @className: CreateExercise
 * @description: 线程创建方式
 * @date: 2022/8/15 18:10
 */
public class CreateExercise {
    public static void main(String[] args) {

    }

    /**
     * 线程创建方式二:
     * 实现Runnable接口
     * run方法定义任务
     * 无返回值
     * 匿名内部类和lambda表达式
     */
    private static void method2() {
        System.out.println("main start");
        // 时间片   15毫秒
        // 匿名内部类
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("threadA执行任务:"+i);
                }
            }
        });
        // lambda表达式
        Thread threadB = new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println("threadB执行任务:"+i);
            }
        });
        threadA.start();
        threadB.start();
        System.out.println("main end");
    }
    
}

2.3 创建方式三

package com.javasm.thread.create;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author: ShangMa
 * @className: CreateExercise
 * @description: 线程创建方式
 * @date: 2022/8/15 18:10
 */
public class CreateExercise {
    public static void main(String[] args) {
        method3();
    }

    /**
     * 线程创建方式三:
     * 实现Callable接口
     * 线程执行任务,返回结果
     * 使用场景:
     * 让线程执行结束返回结果
     */
    private static void method3() {
        System.out.println("main start");
        // Thread thread = new Thread();
        /**
         * 中间类:
         *  class FutureTask implements RunnableFuture
         *  interface RunnableFuture extends Runnable, Future
         */
        FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>() {
            int sum = 0;

            @Override
            public Integer call() throws Exception {
                // 定义任务
                for (int i = 0; i < 10; i++) {
                    sum += i;
                }
                return sum;
            }
        });
        Thread threadA = new Thread(futureTask);
        // 正确结果
        // threadA.start();
        // 得到结果
        Integer count = 0;
        try {
            /**
             * 获取结果:
             * get():阻塞方法->等待结果,直到拿到结果
             */
            count = futureTask.get();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
        System.out.println("数字之和:" + count);
        // 导致:程序卡死
        // threadA.start();
        System.out.println("main end");
    }
}

3 线程方法

3.1 基础介绍

(1)run():定义线程任务
(2)start():启动线程,告知os调度器准备好了,可以被调度了
(3)getState():线程状态
(4)getName():线程名称
(5)getId():线程ID
(6)getPriority():线程优先级,参考值->优先级值比较高的线程有可能优先执行
(7)activeCount():统计活着的线程个数
(8)sleep():让线程休眠  ***
(9)join():等某线程执行完毕  ***

3.2 案例

package com.javasm.thread.methods;

import java.util.concurrent.TimeUnit;

/**
 * @author: ShangMa
 * @className: MethodsExercise
 * @description: 线程方法
 * @date: 2022/8/15 18:11
 */
public class MethodsExercise {
    public static void main(String[] args) {
        System.out.println("main start");
        Thread threadDemoA = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    // currentThread():当前线程
                    System.out.println(Thread.currentThread().getName() + "执行任务:" + i);
                }
            }
        }, "threadA");
        // 启动线程->当前线程准备好,可以被调度了
        threadDemoA.start();
        // RUNNABLE  可运行
        // System.out.println("线程状态:" + threadDemoA.getState());
        // main
        // System.out.println(Thread.currentThread().getName());
        // threadA
        // System.out.println("线程名称:" + threadDemoA.getName());
        // id  数字
        // System.out.println("线程ID:" + threadDemoA.getId());
        // TERMINATED  终止
        // System.out.println("线程状态2:" + threadDemoA.getState());
        // 线程优先级:5    参考值   最小值1 最大值10 普通值5  了解
        // System.out.println("线程优先级:" + threadDemoA.getPriority());
        // System.out.println("线程活着的个数:" + Thread.activeCount());
        // ****

        /**
         * sleep方法:
         *  让线程处于休眠状态
         *  单位毫秒   5秒
         * 使用方式:
         *   Thread.sleep();
         *   TimeUnit.xx.sleep();
         */
        /*try {
            // 让main主线程休眠5秒
            // Thread.sleep(5000);
            // 推荐使用
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }*/

        /*
         * join():
         *  让当前线程死亡   线程执行任务完毕
         *  等待该线程执行完毕
         */
        try {
            threadDemoA.join();
            /**
             * 等待主线程执行完毕
             */
            // 导致:程序卡死
            // Thread.currentThread().join();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("main end");
    }
}

4 多线程数据安全

4.1 案例

4.1.1 Bank

package com.javasm.thread.safe;

/**
 * @author: ShangMa
 * @className: Bank
 * @description: 银行账户类
 * @date: 2022/8/16 14:32
 */
public class Bank {
    // 账户余额
    private double balance;

    /**
     * 存钱
     *
     * @param money
     */
    public void save(double money) {
        /**
         * balance += money
         *
         * balance=balance+money;
         *
         * 机器指令:
         *  获取原先balance值  0.0
         *  执行增加操作
         *  再次给balance赋值
         *
         */
        balance += money;
    }

    /**
     * 取钱
     *
     * @param money
     */
    public void draw(double money) {
        balance -= money;
    }

    public double getBalance() {
        return balance;
    }
}

4.1.2

package com.javasm.thread.safe;

import java.util.concurrent.TimeUnit;

/**
 * @author: ShangMa
 * @className: SafeExercise
 * @description: 多线程数据安全
 * @date: 2022/8/15 18:11
 */
public class SafeExercise {
    public static void main(String[] args) {
        method2();

    }

    /**
     * 多线程数据安全问题引入
     */
    private static void method2() {
        Bank bank = new Bank();
        /**
         * 存钱线程
         */
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    bank.save(100);
                }
                System.out.println(Thread.currentThread().getName()+"执行结束");
            }
        }, "threadA");

        /**
         * 取钱线程
         */
        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10000; i++) {
                    bank.draw(100);
                }
                System.out.println(Thread.currentThread().getName()+"执行结束");
            }
        }, "threadB");
        threadA.start();
        threadB.start();
        try {
            // 让A和B线程先执行,等执行完毕后,再看余额值
            threadA.join();
            threadB.join();

            // TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("balance: " + bank.getBalance());
    }

}

4.2 出现问题

(1)程序中引入多线程,可以提高程序的执行效率
(2)当多个线程同时操作共享主存中的变量值时,会出现数据不安全问题
(3)当某线程执行任务时,执行期间被打断

4.3 JMM

(1)JMMJava Memory ModelJava内存模型     一套规范
(2)JMM给线程划分了工作内存和共享主存
(3)JMM规范:
	A.每个线程都有自己的工作空间   工作内存
	B.线程的局部变量都在各自的工作内存中
	C.各个线程都可访问共享主存中的值,但不能直接修改主存中的值
	D.每个线程的工作空间都是独立的,是互不影响的

4.4 synchronized(重要)

(1)做到原子性:
	原子性:一系列操作要么全部执行要么全部都不执行
	该操作在执行期间不被打断
(2)synchronized修饰方法->同步方法
	同步:一个任务的开始必须要等待另外一个任务的结束
(3)底层机制:
	每一个对象都有唯一的一把对象锁
	同一时刻,一把对象锁只能被一个线程获取到
(4)使用位置:
	普通方法:
		方法声明处使用synchronizedpublic synchronized void save(double money) {}
            线程要想执行方法内容,就必须获取到对象的对象锁;
            当该方法中的内容执行完毕,释放对象锁;->对象锁被其他线程获取到
            锁粒度较大   整个方法
            数据安全     性能低
			
        同步代码块:
        	synchronized (this) {
                balance += money;
            }
            线程要想执行局部代码块中的内容,需要先获取到对象锁;
            当局部代码块中的代码执行完毕,释放对象锁;
            锁粒度较小   局部代码块
            数据安全     性能高
	
    静态方法:
    	方法声明处使用synchronized:
    		线程想执行该方法代码,获取到类对象的对象锁
		局部代码块使用synchronized:
			不能使用this
(5)注意事项:
	内置锁 | 排他锁(互斥锁) | 可重入锁(不用重复获取对象锁);
	遇到异常时,释放对象锁
(6)扩展:
	sleep方法:
		线程不会释放对象锁
	内部同步:
    	Bank:使用synchronized关键字
    外部同步:
    	Bank:不使用synchronized关键字
		测试类:使用synchronized关键字

4.5 ReentrantLock

4.5.1 内容描述

(1)显示锁
(2)ReentrantLock类:
	加锁:lock()
	放锁:unlock()
(3)规范使用:
	借助于异常块使用
	把unlock的调用放到finally(4)显示锁 | 排他锁(互斥锁) | 可重入锁

4.5.2 案例

package com.javasm.thread.safe;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @author: ShangMa
 * @className: Bank3
 * @description: 银行账户类
 * @date: 2022/8/16 14:32
 */
public class Bank3 {
    // 账户余额
    private double balance;
    // 创建对象 ReentrantLock
    private ReentrantLock reentrantLock = new ReentrantLock();

    /**
     * 存钱
     *
     * @param money
     */
    public void save(double money) {
        System.out.println(Thread.currentThread().getName() + "进入save方法");
        // 加锁
        reentrantLock.lock();
        try {
            balance += money;
            System.out.println(1 / 0);
        } finally {
            // 释放锁-必须执行->finally
            reentrantLock.unlock();
        }
        System.out.println(Thread.currentThread().getName() + "执行结束");
    }

    /**
     * 取钱
     *
     * @param money
     */
    public void draw(double money) {
        // 加锁
        reentrantLock.lock();
        balance -= money;
        // 释放锁
        reentrantLock.unlock();
    }

    public double getBalance() {
        return balance;
    }
}

4.6 synchronized和ReentrantLock

(1)synchronized  内置锁
(2)ReentrantLock 显示锁  1.5 
(3)性能基本差不多
(4)比如:集合类  synchronized

5 线程安全类

(1)内部同步:
	Bank  save() draw()
(2)外部同步:
	Bank 没有做任何同步  外部同步
(3)集合类:
	线程安全:vector、hashtable
	线程非安全:ArrayListHashMap
(4)原子性类:
	Atomicxxx
	无锁编程思想:无synchronizedReentrantLock
	CASCompare And Swap,比较并交换
	
	线程将更改后值刷新到主存之前,都会去主存中获取最新值;
	比较;
	即使线程在操作过程被挂起,不影响操作
	
	synchronized:方法声明处 | 局部代码块
	ReentrantLock:比较宽泛
	Atomicxxx:比较局限  AtomicInteger...

你可能感兴趣的:(Java知识体系,java,开发语言)