多线程

进程:

是任务的执行过程,持有资源(共享内存和共享文件)和线程

资源:

是进程和线程的载体


线程:

是系统中最小的执行单元

同一进程拥有多个线程

多个线程共享进程的资源


此图画的关系有些问题,因为thread也实现了Runnable接口


共享变量:

注:必须将共享变量变量声明为volatile

使用volatile变量降低了发生内存一致性错误的风险, 因为任何对volatile变量的写操作都与对该变量的读操作建立了happens-before关系。这种关系意味着对volatile变量值的改变对其他线程总是可见的。更近一步, 当一个线程读取volatile变量的时候,该线程不但读取了最近的变化,而且是导致该变化发生代码的全部影响

如何正确停止线程?

@Java线程——如何正确停止线程

一、错误一:stop()方法

1、not stop:stop()方法会使线程戛然而止

2、使程序突然中止,无法完成完整的业务步骤,也无法进行清理工作

二、错误二:interrupt()方法

1、interrupt()方法只能设置interrupt标志位(且在线程阻塞情况下,标志位会被清除,更无法设置中断标志位),无法停止线程

三、正确方法:设置退出标志

1、使用退出标志位来停止while循环

2、完成最后一次业务后跳出while循环后,之后进行一些清理

interrupt()方法不会中断一个正在运行的线程。这一方法实际上完成的是,在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞的状态。更确切的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态,然后该线程还是继续运行的

线程常用方法

1.获取线程名称:getName();

2.取得当前线程对象:currentThread();

3.判断是否启动:isAlive();

4.强行运行:join();

5.线程休眠:sleep();

6.线程礼让:yield();

什么时候使用thread 什么时候使用runnable

其实两种方式效果一样,但推荐使用runnable

因为Java是单继承,继承了thread就不能继承其他的类

而实现runnable接口的话,扩展性要好很多

@Java线程——线程交互——争用条件

1、当多个线程同时共享访问同一数据(内存区域)时,每个线程都尝试操作该数据,从而导致数据被破坏(corrupted),这种现象称为争用条件

2、原因是,每个线程在操作数据时,会先将数据初值读【取到自己获得的内存中】,然后在内存中进行运算后,重新赋值到数据。

3、争用条件:线程1在还【未重新将值赋回去时】,线程1阻塞,线程2开始访问该数据,然后进行了修改,之后被阻塞的线程1再获得资源,而将之前计算的值覆盖掉线程2所修改的值,就出现了数据丢失情况

线程的交互:互斥与同步

互斥:在同一时间只能有一条线程对关键数据或临界区进行操作

同步:线程之间的一种通信机制

一条线程做了一件事情,然后用某种方式去告诉其它线程:"我做完了"

synchronized关键字实现互斥行为,既可以出现在方法体之上也可以出现在方法体内,以一种块的形式出现。

然后通过lockObject的wait方法(注意:wait的线程被存放在wait set 中)和notifyAll方法实现同步。

步骤:

1.互斥:同一时间,只能有一个线程访问数据

2.同步:通信机制;一个线程完成,以某种方式通知其他线程

3.锁的概念:private final Object lockObj = new Object();

4.互斥实现方式:synchronized关键字

synchronized(lockObj){---执行代码----}加锁操作

lockObj.wait();线程等待状态,以避免线程持续申请锁,不去竞争cpu资源

lockObj.notifyAll();唤醒所有lockObj对象上等待的线程

同步是两个线程之间的一种交互的操作(一个线程发出消息另外一个线程响应)。

同步的实现:wait();notify();notifyAll();这三个方法都是属于Java中的Object对象的成员函数。

调用wait();和notifyAll();方法使线程进入等待或者唤醒不是在同一个线程的同一次操作中执行的,当操作结束,唤醒了所有的等待线程之后,线程又将有着公平的机会竞争CPU资源。

注意:notify();方法唤醒wait set 中的一条线程使其具有竞争CPU的机会,具体唤醒那一条线程是随机的由Java的底层算法决定,我们不能去控制。

通过synchronized关键字为临界区(critical)加锁,这样在线程竞争资源时,当某一条线程获得锁进入临界区后,其他线程将无法再次获取锁进入临界区(critical),直到获得锁的线程退出临界区(critical),释放锁资源。Java的语法保证了同一时间只能有一条线程可以获得lockObject。

java

1.原子性:Java内存模型只保证了基本读取和赋值是原子性操作,如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现。由于synchronized和Lock能够保证任一时刻只有一个线程执行该代码块,那么自然就不存在原子性问题了,从而保证了原子性。

2.可见性:对于可见性,Java提供了volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。

另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

3.有序性

在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。

在Java里面,可以通过volatile关键字来保证一定的“有序性”(具体原理在下一节讲述)。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。

另外,Java内存模型具备一些先天的“有序性”,即不需要通过任何手段就能够得到保证的有序性,这个通常也称为 happens-before 原则。如果两个操作的执行次序无法从happens-before原则推导出来,那么它们就不能保证它们的有序性,虚拟机可以随意地对它们进行重排序。

http://www.cnblogs.com/dolphin0520/p/3920373.html

建议:

1、Java Memory Mode:JMM描述了java线程如何通过内存进行交互,了解happens-before,synchronized,voliatile & final

2、Locks % Condition:锁机制和等待条件的高层实现 java.util,concurrent.locks

3、线程安全性:原子性与可见性,死锁等

4、多线程常用的交互模型

· Producer-Consumer模型

· Read-Write Lock模型

· Future模型

· Worker Thread模型

5、Java5中并发编程工具:java.util.concurrent 线程池ExcutorService Callable&Future BlockingQueue

6、推荐书本:CoreJava & JavaConcurrency In Practice

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容