什么是进程?
- 进程是操作系统中运行的一个任务(一个应用程序运行在一个进程中)。
- 进程(process)是一块包含了某些资源的内存区域。操作系统利用进程把它的工作划分为一些功能单元。
- 进程中所包含的一个或多个执行单元称为线程。进程还拥有一个私有的虚拟地址空间,该空间仅能被它所包含的线程访问。
- 线程只能归属于一个进程并且它只能访问该进程所拥有的资源。当操作系统创建一个进程后,该进程会自动申请一个名为主线程或首要线程的线程。
什么是线程?
- 一个线程是进程的一个顺序执行流。
- 同类的多个线程共享一块内存空间和一组系统资源,线程本身有一个供程序执行时的堆栈。线程在切换时负荷小,因此,线程也被称为轻负荷进程。一个进程中可以包含多个线程。
进程与线程的区别
- 一个进程至少有一个线程。
- 线程的划分尺度小于进程,使得多线程程序的并发性高。另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。线程在执行过程中与进程的区别在于每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的引用来实现进程的调度和管理以及资源分配。
线程使用的场合
- 线程通常用于在一个程序中需要同时完成多个任务的情况。我们可以将每个任务定义为一个线程,使他们得以一同工作。
- 也可以用于在单一线程中可以完成,但是使用多线程可以更快的情况,比如下载文件。
并发原理
- 多个线程“同时”运行只是我们感官上的一种表现。事实上线程是并发运行的,OS将时间划分为很多时间片段(时间片),尽可能均匀分配给每一个线程,获取时间片段的线程被CPU运行,而其他线程全部等待。所以微观上是走走停停的,宏观上都在运行。这种现象叫并发,但不是绝对意义上的“同时发生”。
使用Thread创建并启动线程
- Thread类是线程类,其每一个实例表示一个可以并发运行的线程。我们可以通过继承该类并重写run方法来定义一个具体的线程。
- 其中重写run方法的目的是定义该线程要执行的逻辑。
- 启动线程时调用线程的start()方法而非直接调用run()方法。
- start方法会将当前线程纳入线程调度,使当前线程可以开始并发运行。
- 当线程获取时间片段后会自动开始执行run方法中的逻辑。
编写一个程序,该程序可以每隔一秒,在控制台输出“hello world!”
class AThread extends Thread{
int times;
public AThread(int times){
this.times = times;
}
public void run(){
int i=0;
while(i<times){
i++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello world!");
}}}
public class ThreadDemo1 {
public static void main(String[] args) {
AThread athread = new AThread(10);
athread.start();
}
}
使用Runnable创建并启动线程
- 实现Runnalbe接口并重写run方法来定义线程体,然后在创建线程的时候将Runnable的实例传入并启动线程。
- 优势:
1.Runnable方式可以避免Thread方式由于Java单继承特性带来的缺陷
2.Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况
编写一个程序,该程序可以每隔一秒,在控制台输出“hello world!”
class BThread implements Runnable{
int times;
public BThread(int times){
this.times = times;}
public void run() {
int i=0;
while(i<times){
i++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello world");
}}}}
public class ThreadDemo2 {
public static void main(String[] args) {
BThread bthread = new BThread(10);
Thread thread1 = new Thread(bthread);
thread1.start();
}
}
创建多线程
编写一个程序,该程序可以接收一个整数n,创建一个线程计算从1+2+…+n并输出结果,另一个线程每隔一秒在控制台输出:“我是一个线程,正在输出第*个hello world!”*要用具体的数字显示,这两个工作,要同时运行。
class CThread implements Runnable{
int times;
public CThread(int times){
this.times = times;
}
}
@Override
public void run() {
int i=0;
while(true){
i++;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是一个线程,正在输出第"+i+"个hello world");
if(i==times){
break;
}}}
class DThread implements Runnable{
int times;
public DThread(int times){
this.times = times;
}
int result;
}
@Override
public void run() {
int i=0;
while(true){
result +=++i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("运算结果是"+result);
if(i==times){
System.out.println("最终结果是"+result);
break;
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) {
CThread cthread = new CThread(10);
Thread thread1 = new Thread(cthread);
DThread dthread= new DThread(10);
Thread thread2 = new Thread(dthread);
thread1.start();
thread2.start();
}
}
获取线程信息
-
Thread提供了获取线程信息的相关方法:
-long getId():返回该线程的标识符
-String getName():返回该线程的名称
-int getPriority():返回线程的优先级
-Thread.state getState():获取线程的状态
-boolean isAlive():测试线程是否处于活动状态
-boolean isDaemon():测试线程是否为守护线程
-boolean isInterrupted():测试线程是否已经中断
线程优先级
- 线程的切换是由线程调度控制的,我们无法通过代码来干涉,但是我们可以通过提高线程的优先级来最大程度的改善线程获取时间片的几率。
- 线程的优先级被划分为10级,值分别为1-10,其中1最低,10最高。线程提供了3个常量来表示最低,最高,以及默认优先级:
-Thread.MIN_PRIORITY //1
-Thread.MAX_PRIORITY //10
-Thread.NORM_PRIORITY //5
void setPriority(int priority);设置线程的优先级
sleep方法
- Thread的静态方法sleep用于使当前线程进入阻塞状态:
-static void sleep(long ms) - 该方法会使当前线程进入阻塞状态指定毫秒,当阻塞指定毫秒后,当前线程会重新进入Runnable状态,等待分配时间片。
- 该方法声明抛出一个InterruptException.所以在使用该方法是需要捕获这个异常。
yield方法
- Thread的静态方法yield:
-static void yield() - 该方法用于使当前线程主动让出当次CPU时间片回到Runnable状态,等待分配时间片。
join方法
- Thread的方法join:
-void join() - 该方法用于等待当前线程结束。
- 该方法声明抛出InterruptException.
synchronized关键字
- 多个线程并发读写同一个临界资源时会发生“线程并发安全问题”
- 常见的临界资源:
-多线程共享实例变量
-多线程共享静态公共变量 - 若想解决线程安全问题,需要将异步的操作变为同步操作。
-异步操作:多线程并发的操作,相当于各干各的。
-同步操作:有先后顺序的操作,相当于你干完我再干。
synchronized关键字是java中的同步锁
wait和notify
- 多线程之间需要协调工作。
- 例如,浏览器的一个显示图片的displayThread想要执行显示图片的任务,必须等待下载线程downloadThread将该图片下载完毕。如果图片还没有下载完,displayThread可以暂停,当downloadThread完成了任务后,再通知dispalyThread“图片准备完毕,可以显示了”,这时,displayThread继续执行。
- 以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/notify.等待机制与锁机制是密切关联的。
- 调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)
- 调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;