您的位置:首页 > 编程语言 > Java开发

java线程总结(一)—java疯狂讲义学习笔记

2014-12-05 18:38 363 查看
Java线程总结:

1.线程的概念

1.1先理解进程

一个任务就是一个程序,每个运行中的程序就是一个进程。
进程是表示资源分配的基本单位,又是调度运行的基本单位。例如,用户运行自己的程序,系统就创建一个进程,并为它分配资源,包括各种表格、内存空间、磁盘空间、I/O设备等。然后,把该进程放人进程的就绪队列。进程调度程序选中它,为它分配CPU以及其它有关资源,该进程才真正运行。所以,进程是系统中的并发执行的单位。
在Mac、Windows NT等采用微内核结构的操作系统中,进程的功能发生了变化:它只是资源分配的单位,而不再是调度运行的单位。在微内核系统中,真正调度运行的基本单位是线程。因此,实现并发功能的单位是线程。
进程和线程的关系

(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。

(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。

(3)处理机分给线程,即真正在处理机上运行的是线程。
(4)线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

1.2 线程

线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。
一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。
线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
一个线程可以创建和撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。
线程也有就绪阻塞运行三种基本状态。每一个程序都至少有一个线程,若程序只有一个线程,那就是程序本身。
多线程:线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程

2.为什么要有线程?

(1)易于调度。

(2)提高并发性。通过线程可方便有效地实现并发性。进程可创建多个线程来执行同一程序的不同部分。

(3)开销少。创建线程比创建进程要快,所需开销很少。

(4)利于充分发挥多处理器的功能。通过创建多线程进程(即一个进程可具有两个或更多个线程),每个线程在一个处理器上运行,从而实现应用程序的并发性,使每个处理器都得到充分运行。

3.如何实现线程?

3.1 线程的五种状态:

新建: 当程序使用New关键字创建了一个线程后,线程就处于新建状态。此时它和其它java对象一样,仅仅由Java虚拟机分配内存,并初始化其变量的值。此时的线程对象没有表现出任何线程的动态特征,程序也不会执行线程的线程执行体。

就绪: 当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。

就绪状态只是表示线程可以运行,但它并没有开始运行。

运行: 线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。如果一个计算机只有一个CPU,那么在任何时刻只有一个线程处于运行状态。而在多处理的机器上,会有多个线程并行。

阻塞: 线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。被阻塞的线程会在合适的时候重新进入到就绪状态(不是运行态),也就是说,阻塞解除后,必须重新等待线程调度器再次调度它。

死亡: 如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪,该线程不可再次作为线程执行。

3.2 线程控制:

(1)join线程

(2)后台线程

为其它线程提供服务,又称“守护线程”。比如JVM的垃圾回收线程。

如果所有前台线程死亡,那么后台线程自动死亡。

(3)线程睡眠

让正在执行的线程暂停一段时间,并进入阻塞状态。

(4)线程让步

yield()只是让当前线程暂停一下,让系统的线程调度器重新调度一次。

(5)改变线程优先级

利用setPrioriyt,getPriority()来设置线程的优先级,高优先级的会获得更多的执行机会。

3.3 线程同步

3.3.1 线程安全问题

一个对象是否需要是线程安全的,取决于他是否被多个线程访问。

3.3.2同步代码块

指定一个对象做为锁。

3.3.3同步方法

互斥性:一个操作不一定是原子操作,比如++count,读取count的值,加1,然后将计算结果写入count。如果两个线程在没有同步的情况下对一个计数器执行递增操作,就会出现计数器值偏差。

可见性:

由于重排序(在没有同步的情况下,编译器、处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整), 确保一个线程修改了对象状态后,另一个线程能够看到发生的状态的变化。

锁:

类锁:类锁是用于类的静态方法或者一个类的class对象上的。

对象锁:用于一个对象实例上。
类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的

3.3.4 释放同步监视器的锁定

3.4 线程通信

3.4.1 传统的线程通信方法

认识Object.wait()方法:

导致当前线程等待,直到该对象的notify或notifyAll被执行。换句话说,这个方法行

为效果完全与简单调用wait(0)一样。当前线程必须拥有对象监视器。线程释放对象监视器的所有权,等待直到另一个线程通过调用notify或notifyAll来通知等待对象监视器的线程们并唤醒。然后,当前线程等待重新获取对象监视器并继续执行。因为在一个参数的版本中,中断与伪唤醒是可能的,这个方法应该通常在一个循环中使用:

synchronized (obj) {

while (<condition does not hold>)

obj.wait();

... // Perform action appropriate to condition

}

该方法必须同步执行的,否则会抛出IllegalMonitorStateException。

认识Object.wait(long)方法:

基本功能与Object.wait()相同,只是在指定时间间隔后,当前线程自动唤醒,并与其它线程竞争对象监视器,在获取对象监视器后,当前线程继续向前执行。

认识Object.notify方法:

唤醒等待这个对象的监视器的单一线程。如果有很多线程在等待该对象监视器,则选择其中的一个来唤醒。这歌选择是任意的,与具体的实现相关。一个线程调用wait方法后就等待对象监视器。被唤醒的线程将在当前线程放弃对象锁后才能继续执行,它在竞争该对象锁方面没有任何可靠的特权或劣势。调用这个方法必须占有对象监视器(

因为这个称为monitor的对象锁在同一时刻只有一个对象能获取该锁。wait方法可以理解为交出该对象锁,notify方法可以理解为把该对象锁交出,让所有线程一起去竞争这个锁,那么无论哪个方法,要交出对象锁显然都要先获取该对象锁,因为它是唯一的。

)。线程通过3中途径可以占有对象监视器:

(1)执行对象的同步方法;

(2)执行锁定对象的同步语句块;

(3)执行类对象的静态同步方法。

在某一时刻,只有一个线程占有对象监视器。该方法必须同步执行的,否则会抛出IllegalMonitorStateException。

<pre name="code" class="java">package thread_test;

import java.util.concurrent.TimeUnit;

public class ThreadTest {

final Object synObj = new Object();

public ThreadTest(){
t1.start();
t2.start();
}

Thread t1 = new Thread(new Runnable() {

@Override

public void run() {

synchronized(synObj) {

System.out.println("T1获取synObj的对象监视器,开始执行同步块");

try {

// TimeUnit.MINUTES.sleep(1);
Thread.sleep(10*1000);
System.out.println("T1在 wait()时挂起了");

synObj.wait();

System.out.println("T1被T2唤醒后并重新获得synObj的对象监视器,继续执行");

}catch(InterruptedException e) {

e.printStackTrace();

}

System.out.println("T1获取synObj的对象监视器,结束同步块");

}

};

});

Thread t2 = new Thread(new Runnable() {

@Override

public void run() {

System.out.println("T2启动,但是因为T1占用了synObj的对象监视器,则等待T1执行synObj.wait来释放它");

synchronized(synObj) {

try {

System.out.println("在T1执行synObj.wait后,T2获取synObj的对象监视器,进入同步块");

synObj.notify();

System.out.println("T2执行synObj.notify(),T1被唤醒,但T2还在同步块中,没有释放synObj的对象监视器,T1等待synObj的对象监视器");

// TimeUnit.MINUTES.sleep(1);
Thread.sleep(10*1000);

System.out.println("T2结束同步块,释放synObj的对象监视器,T1获取到synObj的对象监视器,并执行wait后面的操作");

}catch(InterruptedException e) {

e.printStackTrace();

}

}

};

});

public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadTest threadTest = new ThreadTest();
}

}




输出:

T1获取synObj的对象监视器,开始执行同步块

[b]T2
[/b]启动,但是因为T1占用了synObj的对象监视器,则等待T1执行synObj.wait来释放它

[b]T1
[/b]在 wait()时挂起了在T1执行synObj.wait后,T2获取synObj的对象监视器,进入同步块

[b]T2
[/b]执行synObj.notify(),T1被唤醒,但T2还在同步块中,没有释放synObj的对象监视器,T1等待synObj的对象监视器

[b]T2
[/b]结束同步块,释放synObj的对象监视器,T1获取到synObj的对象监视器,并执行wait后面的操作

[b]T1
[/b]被T2唤醒后并重新获得synObj的对象监视器,继续执行

[b]T1
[/b]获取synObj的对象监视器,结束同步块

注意,对象监视器就是对象锁,在object.notify()后,被唤醒的线程还不能立即执行,必须等待到对象锁。

3.4.2 使用Condition控制线程通信

3.4.3使用阻塞队列控制线程通信



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: