Java并发编程学习经验

2020.2.12

一.线程

什么是线程安全:一个类在多个线程同时以任意方式调用时,并且不需要外部额外同步和协同的情况下,任然可以保持内部数据正确并且表现正确的行为,那么这个类就是线程安全的。

不可变的对象一定是线程安全的:1.final修饰的不可变类,如String,Integer。 2.enum枚举(因为枚举被final修饰,具体实现中,他会创建多个对象)

注意:final修饰的类,不一定就是线程安全的。final只能保持你的值是不能直接被覆盖。但是如果用final不会访问到“部分创建“的对象。(如图下用final修饰的线程不安全的list,如图即使使用final 修饰了List,但是对于这个list的add来说,他任然可以被多个线程同时访问,并且进行add操作等)。

public final List<Integer> finalList = new ArrayList<Integer>;

finalList .add(1);
finalList .add(12);

java.util包提供的线程安全的类:ConcurrentHashMap,LinkedBlockingQueue

2020.2.16

二.线程的同步和异步(得到结果的方式)

同步:阻塞式的调用,调用方必须等待响应方执行完毕才会返回。

异步:非阻塞式的调用,会立即返回,调用方不无需等待响应方返回,响应方会通过状态,通知或者回调来通知调用方。如:(1耗时任务,2电商下单链路的非核心链路)

三:阻塞与非阻塞(等待结果时的状态)

阻塞:调用结果返回之前,当前线程被挂起的状态,调用线程只有在得到结果之后才会返回

1 int port=8080;
2 ServerSocket server = new ServerSocket ();
3 Socket socket = server.accept();

非阻塞:非阻塞的调用是指在不能立即得到结果之前,该调用不会阻塞当前线程,而会立即返回。

四:1.并行具有并发的含义,并发不一定是并行的 

  2.多核处理器处理能力要强于单核处理器,并且有更少的系统调度和线程的上下文切换。(效率会更高)

注意:并发的时候,如果是一个处理器,处理多个线程,那么看起来是同时进行的,但是其实并发会对多个线程之间的上下文进行切换,来执行完整的逻辑。

五:线程的状态 

在Thread.class中可以看到线程的六个状态

 1 public static enum State {
 2     /**
 3      * A Thread which has not yet started.
 4      */
 5     NEW,
 6     /**
 7      * A Thread which is running or suspended.
 8      */
 9     RUNNABLE,
10     /**
11      * A Thread which is blocked on a monitor.
12      */
13     BLOCKED,
14     /**
15      * A Thread which is waiting with no timeout.
16      */
17     WAITING,
18     /**
19      * A Thread which is waiting with a timeout.
20      */
21     TIMED_WAITING,
22     /**
23      * A thread which is no longer alive.
24      */
25     TERMINATED 
}

1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
2. 运行(RUNNABLE):Java线程中将就绪(ready)和运行中(running)两种状态笼统的称为“运行”。
线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
3.阻塞(BLOCKED):表示线程阻塞于锁。
4.等待(WAITING):进入该状态的线程需要等待其他线程做出一些特定动作(通知或中断)。
5.超时等待(TIMED_WAITING):该状态不同于WAITING,它可以在指定的时间后自行返回。

6. 终止(TERMINATED):表示该线程已经执行完毕。

 

注意:如何进入到BLOCKED状态,如果两个线程同时竞争一把锁。其中么有获得锁的线程就为BLOCKED状态。

2020.3.6

Thread.yieId(),线程让步,调用这个方法,当前线程会退出CPU时间片。

Thread.sleep(),不会释放当前持有的锁。

六:线程死锁

1.jps:列举正在运行的虚拟机进程并显示虚拟机执行的主类及进程的唯一ID

jstack:用于JVM当前线程的线程快照,得到JVM当前每一条线程的堆栈信息,定位线程长时间的卡顿问题,如死锁,死循环等。

 

七:内存模型

(在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递。Java的并发采用的是共享内存模型,Java线程之间的通信总是隐式进行,整个通信过程对
程序员完全透明。在共享内存的并发模型里,线程之间共享程序的公共状态,通过写-读内存中的公共状态进行隐式通信)
(在Java中,所有实例域、静态域和数组元素都存储在堆内存中,堆内存在线程之间共享。)
(重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序)
(happens-before:在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系)
(与程序员密切相关的happens-before规则如下。
·程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
·监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
·volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的
读。
·传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。)

2020.3.10

八:Synchronized

Synchronized修饰方法:1.方法使用ACC_Synchronized标识。2.如果是static方法,锁作用在类上。3.如果是非static方法,锁作用在具体的对象上

Synchronized修饰代码块:使用monitorenter和monitorexit指令控制线程进出

ReentrantLock 和 Synchronized

    相同点:都是用于多线程中对资源进行加锁,控制代码在同一个时间段只有单个线程执行。当一个线程获取了锁,其他线程都需要阻塞等待

 不同点:    1.Synchronized是JAVA语言的关键字,由虚拟机字节码指令实现。

       ReentrantLock是JAVA sdk提供的API级别的锁              

           2. Synchronized  可以在方法级别加锁,ReentrantLock不行

      3.  ReentrantLock可以通过tryLock等待指令时间的锁,超时返回。

      4.  ReentrantLock提供了公平锁和非公平锁,Synchronized 只有非公平锁。

九:volatile关键字

                       volatile只保证线程间的内存可见性,不具备锁的特性,无法修饰对象的原子性

   2020.3.29                                                  

 十:线程创建的几种方式

1.通过Runnable的run方法来创建线程

Runnable runnable = new Runnable(){
    @Overrive
      public void run(){
    }        
}    

Thread thread = new Thread (runnable);
thread.start();

2.继承Thread类创建(extends)

 

3.使用Callable和FutureTask创建线程

  1实现Callable接口,重写call方法

  2传入Callable对象。构建FutureTask(runnable的子类)对象

  传入FutureTask对象构建Thead对象,启动。FutureTask继承了Runnable

 1 Callable<String> callable = new MyCallable();
 2 FutureTask<String> futureTask= new FutureTask(callable );
 3 
 4 
 5 new Thread(futureTask).start();
 6 
 7 
 8 public static class MyCallable implements Callable<String>{
 9     @Overrivew
10      public String Call() throws Exception{
11      retrun "sting";
12     }
13 }

 

4.通过线程池

将Runnable 和Callable 放到线程池ExecutorService中执行

1.实现Runnable /Callable 接口,重写call/run方法

2.构建ExecutorService线程池对象,调用线程池execute或者submit方法来执行线程

3.对于submit提交,使用futurn来获取执行结果

 

start();方法源码:会先判断thread的状态不等于0,也就是不是new状态,负责抛出异常。然后调用private native void  start0();start0会调用run方法。run会调用runnable的重写的run方法

 

十一:ThreadLocal

 

posted @ 2020-02-16 14:47  heart~  阅读(245)  评论(0)    收藏  举报