1:synchronized(保证原子性和可见性)

1.同步锁。多线程同时访问时,同一时刻只能有一个线程能够访问使synchronized修饰的代码块或方法。它修饰的对象有以下几种:

修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象

修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象

修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象

修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用的对象是这个类的所有对象

2:volatile修饰的变量(只保证可见性,不保证原子性,且不阻塞线程,执行效率高)

线程在每次使用的时候都会读取变量修改后的最新的值。所以只有状态真正独立于程序内其他内容时,变量才能使用volatile修饰。

java内存模型中的happen-before原则,两个线程同时修改访问volatile变量时,写操作比读操作优先级高。

实现原理:通过加入内存屏障和禁止指令重排序来实现。对volatile变量读之前会加一个load屏障指令,写之后会加一个store指令。

3:保证线程安全的思路

对非安全的代码进行加锁控制

使用线程安全的类

多线程并发情况下,线程共享的变量改为方法级的局部变量

4:synchronized和lock比较

synchronized是在JVM层面上实现的,可以通过一些监控工具监控synchronized的锁定,在代码执行时出现异常时JVM也会自动释放锁定。

Lock是通过代码实现的,必须在finally{}中调用unLock()释放锁。

synchronized是读写,读读,写写操作全互斥(性能不高…),使用lock可以提升性能,如:使用读锁锁住读的代码块,写锁锁住写的代码块。

synchronized取得的锁都是对象,而不是把一段代码或函数当作锁。

每个对象只有一个锁(lock)和之相关联。

避免无谓的同步控制(实现同步需要很大的系统开销且使用不当会造成死锁)。

5:死锁

两个或多个进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。

产生死锁的原因:

因为系统资源不足

进程运行推进的顺序不合适

资源分配不当

产生死锁的四个必要条件:

互斥条件:所谓互斥就是进程在某一时间内独占资源。

请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。

不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。

循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。

打破产生死锁的四个必要条件中的一个或几个,保证系统不会进入死锁状态。

打破互斥条件。即允许进程同时访问某些资源。但是,有的资源是不允许被同时访问的,像打印机等等,这是由资源本身的属性所决定的。所以,这种办法并无实用价值。

打破不可抢占条件。即允许进程强行从占有者那里夺取某些资源。就是说,当一个进程已占有了某些资源,它又申请新的资源,但不能立即被满足时,它必须释放所占有的全部资源,以后再重新申请。它所释放的资源可以分配给其它进程。这就相当于该进程占有的资源被隐蔽地强占了。这种预防死锁的方法实现起来困难,会降低系统性能。

打破占有且申请条件。可以实行资源预先分配策略。即进程在运行前一次性地向系统申请它所需要的全部资源。如果某个进程所需的全部资源得不到满足,则不分配任何资源,此进程暂不运行。只有当系统能够满足当前进程的全部资源需求时,才一次性地将所申请的资源全部分配给该进程。由于运行的进程已占有了它所需的全部资源,所以不会发生占有资源又申请资源的现象,因此不会发生死锁。

打破循环等待条件,实行资源有序分配策略。采用这种策略,即把资源事先分类编号,按号分配,使进程在申请,占用资源时不会形成环路。所有进程对资源的请求必须严格按资源序号递增的顺序提出。进程占用了小号资源,才能申请大号资源,就不会产生环路,从而预防了死锁。

6:线程类&池

Thread.sleep()是Thread的静态类方法,谁调用的谁去睡觉,即使在a线程里调用b的sleep方法,实际上还是a去睡觉,要让b线程睡觉要在b的代码中调用sleep。

Thread.sleep(0)的作用是“触发操作系统立刻重新进行一次CPU竞争”。

Thread.start()方法只是让线程处于就绪状态,线程真正抢到CPU时间片后才会执行线程,即此时执行run()方法中的代码。

两个线程互为对方的参数,可以在一个线程的构造器中new一个对方线程的对象并start(),则只启动一个线程即可启动两个线程。

创建线程方法:

继承Thread类,重写run方法

实现Runnable接口,重写run方法,实现Runnable接口的实现类的实例对象作为Thread构造函数的target

通过Callable和FutureTask创建线程

通过线程池创建线程

说明:

前两种无返回值,通过重写run方法,run方式的返回值是void。访问当前线程,直接使用this即可获得当前线程。

后两种有返回值,通过Callable接口就要实现call方法,返回值是Object。访问当前线程必须使用Thread.currentThread()方法。

Callable和Runable接口方法基本相同,只不过Callable接口定义的方法可以有返回值,而且可以声明抛出异常而已。Future是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果的接口。必要时可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。

Future接口定义如下:

public interface Future {
boolean cancel(boolean mayInterruptIfRunning);//中断任务
boolean isCancelled(); boolean isDone();//判断任务是否完成
V get() throws InterruptedException,ExecutionException;//获取任务执行结果
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

  

cancel方法用来取消任务,取消成功返回true,取消失败返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。

如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;

如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true

如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false。

isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。

isDone方法表示任务是否已经完成,若任务完成,则返回true;

get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。

同步&异步,阻塞&非阻塞

同步和异步着重点在于多个任务的执行过程中,一个任务的执行是否会导致整个流程的暂时等待;

而阻塞和非阻塞着重点在于发出一个请求操作时,如果进行操作的条件不满足是否会返会一个标志信息告知条件不满足。

CountDownLatch

是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作执行完后再执行。

CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0后,表示所有的线程都已经完成任务,然后在闭锁上等待的线程就可以恢复执行任务。

用法:实现最大的并行性,开始执行前等待n个线程完成各自任务,死锁检测。

CountDownLatch 一组线程等待另一组线程都执行完了再继续执行。CyclicBarrier 使一组线程在一个时间点上达到同步,可以是一起开始执行全部任务或者一部分任务,同时它是可以循环使用的。

Semaphore 只允许一定数量的线程同时执行一段任务。

场景:在一个主线程中,要求有大量(很多很多)子线程执行完之后,主线程才执行完成。多种方式,考虑效率。

1.自定义一个ImportThread类继承自java.lang.Thread,重载run()方法:用一个List属性保存所有产生的线程。这样只要判断这个List是否为空就知道还有没有子线程没有执行完了。

问题:如果线程1开始并且结束了,而其他线程还没有开始,此时runningThreads的size也为0 主线程会以为所有线程都执行完了。

方法:用一个非简单类型的计数器来取代List型的runningThreads,并且在线程创建之前就应该设定好计数器的值。

2.使用CountDownLatch代替MyCountDown,用await()方法代替while(true){…}

深度分析:Java多线程,线程安全,并发包的更多相关文章

  1. 深度分析 Java 的枚举类型:枚举的线程安全性及序列化问题(转)

    写在前面: Java SE5 提供了一种新的类型 Java的枚举类型,关键字 enum 可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能 ...

  2. 深度分析Java的枚举类型—-枚举的线程安全性及序列化问题

    原文:深度分析Java的枚举类型--枚举的线程安全性及序列化问题 枚举是如何保证线程安全的 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和clas ...

  3. Java多线程——线程八锁案例分析

    Java多线程——线程八锁案例分析 摘要:本文主要学习了多线程并发中的一些案例. 部分内容来自以下博客: https://blog.csdn.net/dyt443733328/article/deta ...

  4. 【JVM】深度分析Java的ClassLoader机制(源码级别)

    原文:深度分析Java的ClassLoader机制(源码级别) 为了更好的理解类的加载机制,我们来深入研究一下ClassLoader和他的loadClass()方法. 源码分析 public abst ...

  5. Java多线程——线程之间的同步

    Java多线程——线程之间的同步 摘要:本文主要学习多线程之间是如何同步的,如何使用volatile关键字,如何使用synchronized修饰的同步代码块和同步方法解决线程安全问题. 部分内容来自以 ...

  6. java 多线程—— 线程让步

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  7. java 多线程—— 线程等待与唤醒

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  8. Java多线程--线程及相关的Java API

    Java多线程--线程及相关的Java API 线程与进程 进程是线程的容器,程序是指令.数据的组织形式,进程是程序的实体. 一个进程中可以容纳若干个线程,线程是轻量级的进程,是程序执行的最小单位.我 ...

  9. Java多线程-线程的同步(同步方法)

    线程的同步是保证多线程安全访问竞争资源的一种手段.线程的同步是Java多线程编程的难点,往往开发者搞不清楚什么是竞争资源.什么时候需要考虑同步,怎么同步等等问题,当然,这些问题没有很明确的答案,但有些 ...

  10. Java多线程——线程的优先级和生命周期

    Java多线程——线程的优先级和生命周期 摘要:本文主要介绍了线程的优先级以及线程有哪些生命周期. 部分内容来自以下博客: https://www.cnblogs.com/sunddenly/p/41 ...

随机推荐

  1. JS XMLHttpRequest请求

    前言 我们知道jq的请求非常简短好用,但是其实js原生的请求也不差,并且不用插件更能说明自己本身的技术已经很强了,别人看自己代码一脸懵逼的时候,这时就可以一一解释这些代码的用处,更能让别人敬佩! JS ...

  2. windows10下IntelliJ IDEA使用logback设置日志输出目录

    1.在项目的src/main/resources目录下新建文件:logback-spring.xml 2:在logback-spring.xml中进行如下配置: <?xml version=&q ...

  3. eclipse配置打开选中文件存储的目录快捷配置

    方便同时复制多个包的文件 https://jingyan.baidu.com/article/adc8151353a896f723bf73cd.html

  4. Redis中的跳表

    date: 2020-10-15 14:58:00 updated: 2020-10-19 17:58:00 Redis中的跳表 参考网址1 参考网址2 redis 数据类型 zset 实现有序集合, ...

  5. D. Design Tutorial: Inverse the Problem 解析含快速解法(MST、LCA、思維)

    Codeforce 472D Design Tutorial: Inverse the Problem 解析含快速解法(MST.LCA.思維) 今天我們來看看CF472D 題目連結 題目 給你一個\( ...

  6. python接口自动化测试--数据分离读取Excal指定单元格数据

    上一篇博客讲了怎么批量读取Excal单元格数据,现在咱们说一下怎么读取Excal指定单元格数据. 一.首先建一个Test_Main类 #!/usr/bin/python # -*- coding: U ...

  7. python类变量与成员变量

    类变量与成员变量 关注公众号"轻松学编程"了解更多. ​ 在类中声明的变量我们称之为类变量[静态成员变量], 在__init__()函数中声明的变量并且绑定在实例上的变量我们称之为 ...

  8. 【持续更新】Tomcat低级错误

    0(模板) 所犯错误: 现象: 报错内容: 1 所犯错误: 声明@Webservlet中未加"/"符号: 现象: 能够Connected to server,但是代码报错,且无法顺 ...

  9. insert into select 和select into from 备份表

    一 insert into select要求表必须存在 INSERTINTO order_record SELECT * FROM order_today FORCEINDEX (idx_pay_su ...

  10. 聊一聊C#基本类型

    C#基本类型 闲来无事,重新温习了下C#基本类型.以下讲的基本类型主要是包括基本的值类型类型和string.struct和class不包含其中. C#基本类型------值类型: bool,byte, ...