多线程
2015-06-20 21:29
309 查看
1、概念
进程: 正在执行中的程序。每一个进程都是一个执行顺序,或者控制单元。
线程: 进程中的一个单独的控制单元。
线程的状态: 线程被创建期、运行期、临时期,冻结期,消亡期。
单线程:一个单线程,一旦进入某一过程,必须等待正常的返回,并退出这一过程, 下一个线程才能开始这个过程。
2、线程的标识
Static Thread currenrThread() 返回对当前正在执行的线程对象的引用
String getName() 返回该线程的名称。
void setName(String name) 或者构造方法。
3、 创建线程
1) 继承Thread类执行线程
A、 定义继承Thread的类 【用于存储线程运行时的代码】
B、复写run方法【自定义run方法】
C、启动线程的start方法【启用线程、run方法】。
class Threadtest extends Thread{
private Res r
public void Threadtest(Res r) {
this.r=r
}
public void run() {
.............
}
Thread t1 = new Thread( new Threadtest(res)).start() ;
2) 实现Runnable接口
A、定义类实现Runnable方法,并复写run方法。
B、创建Thread线程对象,并将定义的Runnable子类对象作为参数传给Thread的构造方法。
C、 调用线程的start方法。
4 线程安全【接口模式】
** 处理共享数据时产生【成员变量、公共资源】
** 线程运行的粒子单位是单条语句(比如:i++是两条语句)
显然,对于共享数据的处理,各线程是接替进行的。如果发生错误,说明接替出现问题。实际上,jvm规定了主内存与线程的工作内存。
当线程操作某个对象时,执行顺 序如下:
(1) 从主存复制变量到当前工作内存 (read and load)
(2) 执行代码,改变共享变量值 (use and assign)
(3) 用工作内存数据刷新主存相关内容 (store and write)
线程不能直接为主存中中字段赋值,它会将值指定给工作内存中的变量副本(assign),完成后这个变量副本会同步到主存储区(store- write),
至于何时同步过去,根据JVM实现系统决定。
那么就可能会发生这样的事情:
1):线程a从主存读取x副本到工作内存,工作内存中x值为10
2):线程b从主存读取x副本到工作内存,工作内存中x值为10
3):线程a将工作内存中x加1,工作内存中x值为11
4):线程a将x提交主存中,主存中x为11
5):线程b将工作内存中x值减1,工作内存中x值为9
6):线程b将x提交到中主存中,主存中x为9
这里我们认为多线程处理不同的代码区域,对于处理同一代码区域的多线程有什么意义呢?
首先,无法体现效率;其次,共享变量处理事件必须原子化限制。
java用synchronized关键字做为多线程并发环境的执行有序性的保证手段之一。当一段代码会修改共享变量,
这一段代码成为互斥区或临界区,为了保证共享变量的正确性,synchronized标示了临界区。
5 、锁
一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁);
/***对象不同,所以锁不同,无法同步*/
public class ThreadTest extends Thread {
private int threadNo;
public ThreadTest(int threadNo) {
this.threadNo = threadNo;
}
public static void main(String[] args) throws Exception {
for (int i = 1; i < 10; i++) {
new ThreadTest(i).start();
Thread.sleep(1); // 延迟
}
}
@Override
public synchronized void run() {
for (int i = 1; i < 10000; i++) {
System.out.println("No." + threadNo + ":" + i);
}
}
}
如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。 取到锁后,他就开始执行同步代码(被synchronized
修饰的代码); 线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻
只有一个线程在执行。
总结来说,同步有两种方式,同步块和同步方法:
1)、对于同步的方法或者代码块来说,必须获得对象锁才能够进入同步方法或者代码块进行操作;
2)、如果采用method级别的同步,则对象锁即为method所在的对象,如果是静态方法,对象锁即指method所在的Class对象(唯一);
3)、对于代码块,对象锁即指synchronized(abc)中的abc;
JDK 1.5版本之后,Lock ,Condition , single 。
死锁:A持S1锁,想要S2锁;B持S2锁,想要S1锁。形成僵局
synchornized(S1){
synchornized(S2)
}
synchornized(lS2){
synchornized(S1)
}
Thread A, B;
6、 线程间通信
当 多线程处理同一资源,但是操作动作(run())不同,会出现要求线程间有序进行。、
(1)加锁的原则
1)、多线程代码块涉及Input,Output两个 代码块的Run(),需要加同步锁。
2)、要处理好锁的对象问题【哪个对象】。
3)同步代码块要最小化
4) 单例设计模式
(2) 利用共享数据通信
wait()….notify()/notifyAll()…. //暂停,唤醒
1) 首先在同步代码块中,执行wait..notify的控制操作。
2) 设置控制标签【共享数据flag】。
3) 确定该操作组监视器【锁对象】,一个对象只有一把锁。只有同一个锁上的线程,才能被同一个锁唤醒。
多线程处理时,造成相应的锁数量的增加,给开发带来麻烦。notify,一般唤醒当前线程池中的最早等待的线程【也就是处理同一run() 的线程】。
用notifyAll启动对方线程运行,利用标记控制本方,实现两个线程的交替运行
小知识:
sleep() 和 wait()有什么区别?
sleep 是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用 sleep 不会释放对象锁。
wait是 Object 类的方法,对此对象调用 wait 方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify 方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
进程: 正在执行中的程序。每一个进程都是一个执行顺序,或者控制单元。
线程: 进程中的一个单独的控制单元。
线程的状态: 线程被创建期、运行期、临时期,冻结期,消亡期。
单线程:一个单线程,一旦进入某一过程,必须等待正常的返回,并退出这一过程, 下一个线程才能开始这个过程。
2、线程的标识
Static Thread currenrThread() 返回对当前正在执行的线程对象的引用
String getName() 返回该线程的名称。
void setName(String name) 或者构造方法。
3、 创建线程
1) 继承Thread类执行线程
A、 定义继承Thread的类 【用于存储线程运行时的代码】
B、复写run方法【自定义run方法】
C、启动线程的start方法【启用线程、run方法】。
class Threadtest extends Thread{
private Res r
public void Threadtest(Res r) {
this.r=r
}
public void run() {
.............
}
Thread t1 = new Thread( new Threadtest(res)).start() ;
2) 实现Runnable接口
A、定义类实现Runnable方法,并复写run方法。
B、创建Thread线程对象,并将定义的Runnable子类对象作为参数传给Thread的构造方法。
C、 调用线程的start方法。
4 线程安全【接口模式】
** 处理共享数据时产生【成员变量、公共资源】
** 线程运行的粒子单位是单条语句(比如:i++是两条语句)
显然,对于共享数据的处理,各线程是接替进行的。如果发生错误,说明接替出现问题。实际上,jvm规定了主内存与线程的工作内存。
当线程操作某个对象时,执行顺 序如下:
(1) 从主存复制变量到当前工作内存 (read and load)
(2) 执行代码,改变共享变量值 (use and assign)
(3) 用工作内存数据刷新主存相关内容 (store and write)
线程不能直接为主存中中字段赋值,它会将值指定给工作内存中的变量副本(assign),完成后这个变量副本会同步到主存储区(store- write),
至于何时同步过去,根据JVM实现系统决定。
那么就可能会发生这样的事情:
1):线程a从主存读取x副本到工作内存,工作内存中x值为10
2):线程b从主存读取x副本到工作内存,工作内存中x值为10
3):线程a将工作内存中x加1,工作内存中x值为11
4):线程a将x提交主存中,主存中x为11
5):线程b将工作内存中x值减1,工作内存中x值为9
6):线程b将x提交到中主存中,主存中x为9
这里我们认为多线程处理不同的代码区域,对于处理同一代码区域的多线程有什么意义呢?
首先,无法体现效率;其次,共享变量处理事件必须原子化限制。
java用synchronized关键字做为多线程并发环境的执行有序性的保证手段之一。当一段代码会修改共享变量,
这一段代码成为互斥区或临界区,为了保证共享变量的正确性,synchronized标示了临界区。
5 、锁
一段synchronized的代码被一个线程执行之前,他要先拿到执行这段代码的权限,在java里边就是拿到某个同步对象的锁(一个对象只有一把锁);
/***对象不同,所以锁不同,无法同步*/
public class ThreadTest extends Thread {
private int threadNo;
public ThreadTest(int threadNo) {
this.threadNo = threadNo;
}
public static void main(String[] args) throws Exception {
for (int i = 1; i < 10; i++) {
new ThreadTest(i).start();
Thread.sleep(1); // 延迟
}
}
@Override
public synchronized void run() {
for (int i = 1; i < 10000; i++) {
System.out.println("No." + threadNo + ":" + i);
}
}
}
如果这个时候同步对象的锁被其他线程拿走了,他(这个线程)就只能等了(线程阻塞在锁池等待队列中)。 取到锁后,他就开始执行同步代码(被synchronized
修饰的代码); 线程执行完同步代码后马上就把锁还给同步对象,其他在锁池中等待的某个线程就可以拿到锁执行同步代码了。这样就保证了同步代码在统一时刻
只有一个线程在执行。
总结来说,同步有两种方式,同步块和同步方法:
1)、对于同步的方法或者代码块来说,必须获得对象锁才能够进入同步方法或者代码块进行操作;
2)、如果采用method级别的同步,则对象锁即为method所在的对象,如果是静态方法,对象锁即指method所在的Class对象(唯一);
3)、对于代码块,对象锁即指synchronized(abc)中的abc;
JDK 1.5版本之后,Lock ,Condition , single 。
class BoundedBuffer { final Lock lock = new ReentrantLock(); //定义唯一的一个锁对象, final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition();//为锁对象定义监视器Condition,将监视器与锁绑定。 final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); //拿到锁,开始执行 try { while(true) { while (count == items.length) notFull.await(); // 通过count共有变量实现线程间有序进行,wait()会释放锁 items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } // 唤醒指定线程,返回判读count值,进入等待 } finally { lock.unlock(); // 释放锁。 } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
死锁:A持S1锁,想要S2锁;B持S2锁,想要S1锁。形成僵局
synchornized(S1){
synchornized(S2)
}
synchornized(lS2){
synchornized(S1)
}
Thread A, B;
6、 线程间通信
当 多线程处理同一资源,但是操作动作(run())不同,会出现要求线程间有序进行。、
(1)加锁的原则
1)、多线程代码块涉及Input,Output两个 代码块的Run(),需要加同步锁。
2)、要处理好锁的对象问题【哪个对象】。
3)同步代码块要最小化
4) 单例设计模式
(2) 利用共享数据通信
wait()….notify()/notifyAll()…. //暂停,唤醒
1) 首先在同步代码块中,执行wait..notify的控制操作。
2) 设置控制标签【共享数据flag】。
3) 确定该操作组监视器【锁对象】,一个对象只有一把锁。只有同一个锁上的线程,才能被同一个锁唤醒。
多线程处理时,造成相应的锁数量的增加,给开发带来麻烦。notify,一般唤醒当前线程池中的最早等待的线程【也就是处理同一run() 的线程】。
用notifyAll启动对方线程运行,利用标记控制本方,实现两个线程的交替运行
小知识:
sleep() 和 wait()有什么区别?
sleep 是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用 sleep 不会释放对象锁。
wait是 Object 类的方法,对此对象调用 wait 方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify 方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
相关文章推荐
- UVa120 - Stacks of Flapjacks (STL)
- java笔记(coursera-pku)
- hdu 5273 Dylans loves sequence 逆序数简单递推
- leetcode--Minimum Size Subarray Sum
- cocos2dx常见49种Action
- OpenCV实现图像搜索引擎(Image Search Engine)
- POJ水题1083区间重叠问题
- cocos2d-x 菜鸟实习生学习篇-菜单坐标
- HDU 5273
- Stars(一定要看,树状数组差点问线问题)
- 8.运行及总结
- 【初始化块】 类里的第4种成员(除Field、方法和构造器)
- 事件机制(事件冒泡与事件捕获)
- Bootstrap里的文件作用
- Node.js基础一 环境配置、JS基础、创建WebSite
- 7.测试与调试
- shell小技巧
- 【BestCoder #45】
- php面向对象基本概念(Final关键字)
- java实现七种排序 (插入排序, 希尔排序, 插入排序, 快速排序, 简单选择排序, 堆排序, 归并排序)