进程就是程序的一次执行过程,或是正在运行的一个程序
线程是进程的细化,一个进程可以并行执行多个线程
线程独立拥有虚拟机栈和程序计数器,进程独立拥有方法区和堆,所以线程共享方法区和堆
并行:多个CPU同时执行多个任务
并发:一个CPU同时(同一时间片)执行多个任务
创建继承Thread类的子类
重写Thread类中的run方法 -->里边写线程代码要做的事情
创建线程对象
调用start()方法(启动当前线程,调用当前线程的run方法)
start():启动当前线程;调用当前线程的run()
run(): 通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
currentThread():静态方法,返回执行当前代码的线程
getName():获取当前线程的名字
setName():设置当前线程的名字
yield():释放当前cpu的执行权 //从运行态释放CPU执行权转换到就绪态
暂停当前正在执行的线程对象,并执行其他线程。yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果
join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态
stop():已过时。当执行此方法时,强制结束当前线程。
sleep(long millitime):让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态,其他线程获得执行机会
isAlive():判断当前线程是否存活
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5 -->默认优先级
如何获取和设置当前线程的优先级:
getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级
创建一个实现了Runnable接口的类
实现类去实现Runnable中的抽象方法:run()
创建实现类的对象
将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
通过Thread类的对象调用start()
开发中:优先选择:实现Runnable接口的方式
原因:1.实现的方式没有类的单继承性的局限性,用继承的方式可能本身要继承别的东西,一个类继承Thread不合适
2.实现的方式更适合来处理多个线程有共享数据的情况。
联系:public class Thread implements Runnable
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中
新建-->(调用start())-->就绪-->(获得CPU执行权)--><--(失去CPU执行权)<--运行-->(执行完run();调用线程的stop();出现Error或Exception且未处理)-->死亡
运行-->(sleep(long time);join();等待同步锁;wait();suspend();)-->阻塞
阻塞-->(sleep()时间到;join()结束;获取同步锁;notify()或notifyAll();resume();)-->就绪
同步方式在线程进入同步代码块或者同步方法后,其他线程不能进入,确保了在处理共享资源时只有一个线程进行操作
方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码(操作共享数据的代码) -->不能包含代码多了(效率低了,代码错了),也不能包含代码少了
}
共享数据:多个线程共同操作的数据
同步监视器:俗称锁。任何一个类的对象都可以充当锁
要求:多个线程用同一把锁
Object obj = new Object();
synchronized(obj){}
在实现Runnable接口创建多线程的方式中,可以考虑使用this充当同步的监视器
在使用继承实现多线程的方式中,慎用this充当同步监视器,可以考虑使用当前类充当同步监视器(类名.class)
方式二:同步方法
将操作共享数据的代码放在一个方法中,用synchronized修饰方法
private synchronized show(){} //同步监视器为this,属于对象
在实现Runnable接口创建多线程的方式中,可以直接使用上边的方法定义,因为同步方法的同步监视器是this
private static synchronized show(){} //同步监视器为class,属于类
在使用继承实现多线程的方式中,不能单纯使用同步方法,要加static,这时的同步监视器是当前类.class
同步的方式,解决了线程的安全问题 -->好处
操作同步代码时,只能有一个线程参与,其他线程等待,相当于一个单线程操作 -->局限性
不同线程分别占用对方需要的同步资源不放弃,都在等着对方放弃自己需要的同步资源,就形成了线程的死锁
比如s1握住s1的锁后请求s2的锁,而s2握住s2的锁后也要s1的锁,s1等s2放弃他的资源,s2等s1放弃他的资源,互不相让
实例化ReentrantLock ReentrantLock lock = new ReentrantLock();
try{lock.lock();}
finally{lock.unlock();}包围起来
synchronized 与 Lock的异同?
相同:二者都可以解决线程安全问题
不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器
Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())
wait();一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器 //放在后边
notify();一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被阻塞,就会唤醒优先级高的线程 //放在前边
notifyAll();一旦执行此方法,就会唤醒所有被wait的线程
说明:wait,notify,notifyAll都必须使用在同步代码块和同步方法中
三个方法的调用者是同步代码块中的同步监视器,否则会出现异常
三个方法定义在Object类中
相同点:都可以使当前线程进入阻塞状态
不同点:两个方法声明位置不同,sleep声明在Thread中,wait声明在Object中
sleep可以在任何情况下调用,wait只能在同步代码块中调用
如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁
方式一:实现Callable接口
创建一个实现Callable的实现类
重写call()方法,将线程的操作放入call方法中
创建Callable实现类对象
创建FutureTask对象,将Callable实现类对象传进构造器中
//FutureTask是唯一实现Future接口的实现类
//相比Runnable接口多了这步操作
创建Thread类的对象,将FutureTask对象放进构造器中,执行start方法
调用FutureTask对象的get方法,该get方法的返回值就是call方法的返回值
//对返回值有兴趣可以使用
相对Runnable的优势:
call()方法有返回值
call()方法可以抛异常,被外部捕获了解异常信息
call()方法支持泛型
由于之前的两种方法都不能看run方法中的返回值,实现Callable接口这种方式提供的call方法有返回泛型类型的返回值,但线程不能直接调用call方法的返回值
这时可以使用Future类里面有一个FutureTask的实现类,该类是Future的实现类也实现了Runnable接口,它作为监控call方法,其中的get方法能得到call的返回值
创建销毁线程会占用大量的资源,我们提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,避免频繁创建销毁,实现重复利用
线程池避免线程的重复创建与销毁,线程完成任务后不销毁回到线程池中等待做其他的事情
好处:
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理
corePoolSize:核心池的大小
在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中
maximumPoolSize:最大线程数
表示在线程池中最多能创建多少个线程
keepAliveTime:线程没有任务时最多保持多长时间后会终止
当线程数量大于等于核心线程数就会将缓冲区里创建的那些线程进行放弃,即达到keepAliveTime后放弃
更多线程池的使用请看:http://www.cnblogs.com/dolphin0520/p/3932921.html
在java doc中,并不提倡我们直接使用ThreadPoolExecutor,而是使用Executors类中提供的几个静态方法来创建线程池
Executors.newCachedThreadPool(); //创建一个缓冲池,缓冲池容量大小为Integer.MAX_VALUE
Executors.newSingleThreadExecutor(); //创建容量为1的缓冲池
Executors.newFixedThreadPool(int); //创建固定容量大小的缓冲池
上述的Executors方法实际上还是调用了ThreadPoolExecutor类的构造器,因为参数对我们来说不好设置,所以没用
execute()方法适用于Runnable,他是Executor接口中就有的方法
submit()方法适用于Callable,底层还是调用了executor()方法
1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; //Executors得到的还不是我们线程池ThreadPoolExecutor,需要强转
2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合适用于Runnable
service.execute(new NumberThread1());//适合适用于Runnable
service.submit(Callable callable);//适合使用于Callable
3.关闭连接池
service.shutdown();
原文:https://www.cnblogs.com/tianzhenaichirou/p/14586013.html