1、线程与进程
进程
狭义上讲:正在执行的程序,由线程组成,可包含多个线程在运行。
广义上讲:进程是一个具有一定独立功能的程序有关于某个数据集合的一次运行的活动。它可以申请或者拥有系统资源,是一个动态的概念。
进程的概念主要两点:1.进程是一个实体,每一个进程都有自己的地址空间,一般情况下包括文本区域,数据区和堆栈。
线程
为进程中执行的程序片段。
一个线程由线程ID,当前指令针,寄存器和堆栈组成,另外线程是进程的实体,是被系统独立调试的分派的基本单元。
线程是程序中一个单一的顺序控制流程。进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
2、线程启动的两种方法;为什么会有这两种方法。
线程中有两种启动方法:
(1)一种是继承Thread类,重写run方法
(2) 另一种是实现Runnable接口,实现run方法。
由于java是单继承的,继承一个类后不能继承其他类。用实现runnable接口启动线程,弥补了java单继承。
3、线程的状态(线程的生命周期)
(1) 创建 : 创建线程也就是new线程;
(2)就绪:调用了start()方法,等待cpu调度;
(3)运行:执行了run()方法;
(4) 阻塞:也就当前线程处于是停止状态,cpu调用其他线程执行。
(5) 消亡:线程销毁。在线程里面的代码执行完毕后,线程自动销毁。
4、线程启动是调用start()方法,为什么不是run()方法
线程的启动是通过调用start()方法的。如果直接调调用线程调用了run()方法,那将与调用普通方法没有区别。
java就把run()方法当成一个普通方法执行,先执行完这个方法再去执行其他的。只有调用一start()方法,才能开启线程。
比如在android中创建一个dialog或者toast,如果没有调用show()方法,这个弹窗也不会显示。
5、线程的常用方法;线程的常用方法基本上都在Thread中
(1) 得到当前线程名称:Thread.currentThread().getName()
(2) 取得当前线程对象:currentThread();
(3) 线程是否执行:isAlive()
(4) 线程的强行运行:join();
(5) 线程休眠:sleep();
6、线程的优先级
(1) min_priority: 1
(2) max_priority; 10
(3) norm_priority; 5
在不指定线程优先级的时候,默认优先级为5;
线程的优先级是有可能影响线程的执行顺序,而不是一定按照优先级执行。多线程的执行其实是线程的交换执行
我们提高线程的优先级只是提高了线程执行的概率。比如两个线程的执行的概率都是50%,提高了一个线程概率为60%,另一个则为40%,
但是最终执行的哪个还是靠运气的。比如执行了10次都有可以60%的一次都没执行。
7、线程同步 理解synchronized()关键字
用来给对象,方法,或者代码加锁,当它锁定一个方法或者代码块的时候,同一个时间只能有一个线程执行这段代码。通常用于多个线程访问一个对象的时候使用。它确保了其他线程必须等待当前线程执行完毕后
才能执行这个代码块或者方法。
8、多线程下载一定能提高下载速度吗
多线程只有在多处理器系统(比如多核、smp等)上才有可能提高执行速率。在多处理系统上,将要执行的任务分割成多个可并行执行线程,就可以提高执行速率。但是如果任务本身不可并行,比如就是一个有限状态自动机的程序,那就没办法用到多处理机,也不能提高速率。上面说的是并行,不是并发。单处理器上多线程只能并发执行而不是并行,多线程并不能提高纯计算程序的速率。但是对于一般程序中都有i/o操作等,可以将io操作放到一个线程中执行,从而可以在io同时进行其他操作不比等待io完成,这样可以提高速率。不宜创建过多线程,因为线程context switch造成很大的cpu负荷,并且线程占用内存资源。现在的网络服务器为了支持大量并发多不是靠多线程或多进程,而采用其他的技术如异步i/o
比如手机是8核的,此时就可以适当的多开线程,去处理下载任务,这样可以利用闲置的CPU。
但对于单核的手机或者其他单核系统呢?
有可能会,也有可能反而降低速度。降低速度的原因是一个CPU,去执行多个线程,在线程间来回不断的切换,反而降低性能。但是,很多情况下(多线程),比如A线程正在下载,B准备下载,这样提高下载的并行,从而也减小下载的时间。
9、线程池
(1)线程池的原因
对于java来说频繁的创建和销毁对象是比较耗费内存资源的,每次创建一个线程也是一个对象,因此就产生了线程池。
(2)应用情况:需要大量的线程来完成的工作,并且每个工作需要的时间比较短。
(3)线程池中内部工作:
- 可以规定线程池中线程的数量(也就是最多创建线程的数量),当一个线程执行完一个任务的时候,会接着执行另一个任务,如果这个线程发生异常,则由另一个线程池中的线程来代替
- 如果任务数量多于线程数量,则没有完成的任务处于等待阶段
创建线程池的几种方法
在java5以后,新增了一个Executors工厂类来创建线程池,在java5在以前都是通过自己实现创建线程池。
在Executors中提供了以下几个静态方法来创建线程池。
- (1)newCachedThreadPoole() 创建一个具有缓存功能的线程池。
public static ExecutorService newCachedThreadPool()
- (2)newFixedThradExecutor(int Threads) 创建一个具有固定线程数量的线程池。
public static ExecutorService newFixedThreadPool(int nThreads,ThreadFactory threadFactory)
- (3)newSingleThreadPool()创建一个只有一个线程的线程池。
public static ExecutorService newSingleThreadExecutor()
- (4)newScheduledThreadPool()创建具有指定线程数量的线程池,这个线程池中的线程可以指定延迟时间来执行。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创建线程池的步骤
- 调用Executors类的静态方法创建一个ExecutorService对象,得到一个线程池。
- 创建Runnbaler的实现类
- 通过submit()方法向线程池提交runnbale线程
- 通过shutdown()方法关闭线程池
代码:
1、线程的两种开启方式:
使用继承Thread
public class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 100; i++) {
System.out.println(name+"-----" + i);
}
super.run();
}
}
使用实现runnable接口:
public class MyRunnable implements Runnable{
private String name;
public MyRunnable (String name){
this.name=name;
}
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<100;i++){
//得到当前线程的名称; System.out.println(Thread.currentThread().getName()+"---"+i);
}
}
}
在主函数中的两种调用方式:
public static void main(String[] args) {
// TODO Auto-generated method stub
//使用继承Thread类创建线程
// MyThread t1=new MyThread("thread-A");
// MyThread t2=new MyThread("thread-B");
// t1.start();
// t2.start();
//使用runnable创建线程
MyRunnable r1 = new MyRunnable("runnable-A");
MyRunnable r2 = new MyRunnable("runnable-B");
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
}
还是对于买票的问题,假如3个窗口买10张票:
package com.threadTest;
public class ThreadTest2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyRunnable2 r2=new MyRunnable2();
//new 三个线程,相当于三个窗口买票;
Thread t1=new Thread(r2);
Thread t2=new Thread(r2);
Thread t3=new Thread(r2);
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable2 implements Runnable {
private int ticket=10;
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<10;i++){
if(ticket>0){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("车票"+ticket--);
}
}
}
}
运行结果:出现0,负数。
加上同步锁之后代码:
public class ThreadTest2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyRunnable2 r2 = new MyRunnable2();
// 相当于三个窗口买票;
Thread t1 = new Thread(r2);
Thread t2 = new Thread(r2);
Thread t3 = new Thread(r2);
t1.start();
t2.start();
t3.start();
}
}
class MyRunnable2 implements Runnable {
private int ticket = 10;
public void run() {
// TODO Auto-generated method stub
for (int i = 0; i < 10; i++) {
synchronized (this) {
if (ticket > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("车票" + ticket--);
}
}
}
}
}
这样运行后不会再出现负数和0;
创建线程池demo
package com.thrad.demo;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThradTest {
public static void main(String[] args) {
//创建一个具有固定线程数量为3的线程池;
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 100; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"执行的"+index);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
}
}
运行结果:在pool1的线程池中只有三个线程执行新的任务。