您的位置:首页 > 其它

多线程

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 。



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)后本线程才进入对象锁定池准备获得对象锁进入运行状态。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: