1.进程与线程
程序:是指令和数据的有序集合,是一个静态的概念
进程:是程序在处理机上的一次执行过程,是一个动态的概念,有自己的地址空间
进程的状态:执行时有间断性,决定进程可能有多种状态:
1.就绪态
2.运行态
3.阻塞态
就绪->(进程调度算法)->运行->(IO事件执行)->阻塞->(事件结束)->就绪->(时间片用完)->执行
线程:轻量级进程,是进程的一个执行路径,共享一个内存地址,线程之间可以*切换,并发执行,一个进程最少有一个线程。
并行:就是两个任务同时运行(多个CPU)
并发:指两个任务同时请求运行,而处理器一次只能接收一个任务,就会把两个任务安排轮流执行,由于CPU切换时间片较短,会感觉同时进行。
2.线程的基本使用
线程实现的两种方式:
1.继承thread类:
class MyThread extends Thread{
public void run(){
//逻辑处理
}
}
MyThread mt = new MyThread();
mt.start();
2.实现Runable接口:
class MyRunnable implements Runnable{
public void run(){
//逻辑处理
}
}
MyRunnable mr = new MyRunnable();
Thread t = new Thread(mr);
t,start();
3.线程休眠
public static void sleep(long millis);
throws interruptedException使当前正在执行的线程以指定的毫秒数暂停,释放CPU的时间片,具体取决于系统定时器和调度程序的精度和准确性。线程不会丢失任何显示器的所有权。
参数:millis-以毫秒为单位的睡眠时间长度
异常:
-millis为负数时:illegalArgumentException
任何线程中断当前线程,当抛出此异常时,当前线程的中断状态将被清除:InterruptedException
线程的休眠:在当前线程的执行中,暂停指定的毫秒数,释放CPU时间片。
eg:
try{
Thread.sleep(millis:500);
}catch(InterruptedException e){
e.printStackTrace();
}
4.join与中断线程
public final void join()throws interruptedException
-等待线程死亡。
异常interruptedException:如果任何线程中断当前线程。当抛出此异常时,当前线程的中断状态将被清除。
public void interrupted()
-中断这个线程
除非当前线程中断自身,这是始终允许的
public static boolean interrupted()
测试当前线程是否中断。该方法可以清除线程的中断状态。
join方法:
加入线程,让调用的线程先执行指定时间或执行完毕
中断线程:
1.使用interrupt方法来中断线程,设置一个中断状态
2.自定义标记方式
demo:主线程可以等子线程执行完返回数据后继续执行
public class ThreadDemo{
public static void main(String[] args){
MyRunnable2 mr = new MyRunnable2();
Thread t = new Thread(mr);
t.start();
for(int i = 0;i < 50;i++){
System.out.peintln(Thread.currentThread().getName()+"--"+i);
try{
Thread.sleep(millis:300);
}catch(interruptedException e){
e.peintStackTrace();
}
if(i==20){
t.join(); //让t线程执行完
}catch (interruptedException e){
e.peintStackTrace();
}
}
}
}
5.守护线程与yield
public final void setDaemon(boolean on):
将此线程标记为daemon线程或用户线程。当运行的唯一线程都是守护进程线程时,java虚拟机将退出。
public final boolean is Daemon():
测试这个线程是否是守护线程。
public static void yield() //让出CPU时间片
暂停当前正在执行的线程对象,并执行其他线程。
eg:
public class ThreadDemo{
public static void main(String[] args){
MyRunnable mr = new MyRunnable();
Thread t = new Thread();
//线程可以分为守护线程和用户线程,当进程中没有用户进程时,JVM会退出。
t.setDaemon(true); //把线程设置为守护线程
t.start();
for(int i = 0;i < 50;i++){
设置sleep属性:200
System.out.println("main--"+i);
}
}
}
class MyRunnable implements Runnable{
public void run(){
for(int i = 0;i < 50;i++){
//设置sleep属性 500
System.out.println("--"+i);
}
}
}
6.其他方法与优先级
long getid() //返回该线程的标识符
String getname() //返回该线程的名称
void setName(String name) //改变线程名称,使之与参数name相同
boolean isAlive() //测试线程是否处于活动状态
void setPriority(int newPriority) //更改线程的优先级
static int MAX_PRIORITY //线程可以具有的最高优先级
static int MIN_PRIORITY //线程可以具有的最低优先级
static int NORM_PRIORITY //分配给线程的默认优先级
7.线程同步
多线程共享数据:
在多线程的操作中,多个线程可能同时处理同一个资源。
线程同步:
解决数据共享的问题,必须使用同步,所谓同步就是多个线程在同一个时间段内只能有一个线程执行指定代码,其他线程要等待此线程完成之后可以继续执行。
线程同步方法:
1.同步代码块:
synchronized(要同步的对象){
要同步的操作;
}
2.同步方法:
public synchronized(){
要同步的操作;
}
3.lock(ReentrantLock)
eg:
/**
1.多线程同时访问资源,会发生线程不安全的情况
2.多线程共享数据必须使用同步
3.实现同步的三种方法:使用同步代码块;使用同步方法;使用lock。
多线程共享数据,会有安全问题,使用同步可以解决安全问题,但同时会牺牲性能,所以同步的代码块要尽量保持简短,把不随数据变化的相关代码移除同步块。不要阻塞。在持有锁的时候,不要对其他对象调用方法。
*/
public class ThreadDemo{
public static void main(String[] args){
MyRunnable mr = new MyRunnable();
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
t1.start();
t2.start();
}
}
class MyRunnable implements Runnable{
private int ticket = 10;
public void run(){
for(int i = 0;i < 300;i++){
if(ticket>0){
ticket--;
try{
Thread.sleep(millis:1000);
}catch (interruptedException e){
e.printstackTrace();
}
System.out.println(and enough ticket+"张");
}
}
}
}
注意:::::以上会出现错误
解决:
private int ticket = 10;
private object obj = new object();
if(ticket>0){
synchronized(obj){
ticket--;
try{
Thread.sleep(millis:1000); //sleep不会丢失监视器的所有权(锁)
}catch (interruptedException e){
e.printstackTrace();
}
System.out.println(and enough ticket+"张");
}
}
/*用lock实现,互斥锁*/
ReentrantLock lock = new ReentranLock();
private void method(){
lock.lock();
if(ticket>0){
synchronized(obj){
ticket--;
try{
Thread.sleep(millis:1000); //sleep不会丢失监视器的所有权(锁)
}catch (interruptedException e){
e.printstackTrace();
}
System.out.println(and enough ticket+"张");
}
lock.unlcok();
}
8.死锁
过多的同步有可能出现死锁,死锁的操作一般是在程序运行的时候才有可能出现。
http://video.mobiletrain.org/course/index/courseId/401
9.生产者与消费者应用案例
/*
两个线程协同工作,先生产,再消费,交替进行
注意:wait一定要在有同步方法中使用。
面试题:sleep与wait的区别?
->sleep让线程进入休眠状态,让出CPU的时间片,但是不会释放对象监视器的所有权(对象锁)
->wait让线程进入等待状态,让出CPU的时间片,并释放对象监视器的所有权,等待其他线程通过notify方法来唤醒。
*/
public class producterCustmerDemo{
public static void main(String[] args){
Food food = new Food();
Producter p = new Producter(food);
Customers c = new Customers(food);
Thread t1 = new Thread(p);
Thread t2 = new Thread(c);
t1.start();
t2.start();
}
}
/*消费者*/
class Customer implements Runnable{
private Food food;
public Customers(Food food){
this.food = food;
}
public void run(){
for(int i = 0;i < 20;i++){
food.get();
}
}
}
/*生产者*/
class Producter implements Runnable{
private Food food;
public producter(Food food){
this.food = food;
}
public void run(){
for(int i = 0;i < 20;i++){
if(i%2==0){
food.set("iphone","x");
}else{
food.set("huawei","pro");
}
}
}
}
/*
Food
*/
class Food{
private String name;
private String desc;
private boolean flag = true; //true表示可以生产,false表示可以消费,解决交替问题
/*生产产品*/
public synchonrized void set(String name,String desc){ //设置同步解决生产错乱问题
//flag为flase,表示盘子已满不能生产
if(!flag){
//不仅时间片要给出,锁的监视权也要释放
//注意sleep不会释放监视器的所有权,wait会释放监视器的所有权(锁)
try{
this.wait(); //等待线程进入等待状态,释放监视器的所有权(对象锁)
}catch(InterruptedException e){
e.printStackTrace();
}
}
this.setName(name);
try{
Thread.sleep(millis:500);
}catch(InterruptedException e){
e.printStackTrace();
}
this.setDesc(desc);
flag = false;
this.notify(); //唤醒等待的线程(随机唤醒其中的一个)
}
/*消费产品*/
public synchonrized void get(){
//false表示盘子有东西,可以消费,ture表示没有,不能消费
if(flag){
try{
this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
try{
Thread.sleep(millis:500);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println(this.getName()+"->"+thisDesc());
flag = true;
this.notify(); //唤醒一个线程,注:notifyall为唤醒所有线程
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public String getDesc(){
return desc;
}
public void setDesc(String desc){
this.desc = desc;
}
public String toString(){
return "Food{"+"name="+name+"\'"+",desc='"+desc+"'\"+'}';
}
public Food(String name,String desc){
this.name = name;
this.desc = desc;
}
public Food(){
}
}
10.线程生命周期
如图:桌面
11.线程池
线程池是预先创建线程的一种技术。线程池在还没有任务到来之前,创建一定数量的线程,放入空闲的队列中,然后对这些资源进行复用。减少频繁的创建和销毁对象。
线程池接口是ExrcutorService。
java.util.concurrent:并发编程中很常用的实用工具类。
Executor接口:执行已提交的Runnable任务的对象。
Executors类:此包中所定义的Executor,ExecutorService等的工厂和实用方法。
ExecutorService接口:Executor提供了管理终止的方法,以及可为跟踪一个或多个异步任务执行状况而生成Future的方法。
在Executors类里面提供了一些静态工厂,生成一些常用的线程池。
1.newSingleThreadExecutor:
创建一个单线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
2.newFixedThreadPool:
创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
注:线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么新的线程池会补充一个新线程。
3.newCachedThreadPool:
创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
4.newScheduledThreadPool:
创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
eg:jdk1.5创建线程池的4种方法
1.创建一个单线程的线程池
public class ThreadDemo{
public static void main(String[] args){
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(new MyRunnable());
es.execute(new MyRunnable());
es.shutdown();
}
}
class MyRunnable implements Runnable{
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("run--"+i);
try{
Thread.sleep(300);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
2.创建固定大小的线程池.
public class ThreadDemo{
public static void main(String[] args){
ExecutorService es = Executors.newFixedThreadPool(nThreads:2);
es.execute(new MyRunnable());
es.execute(new MyRunnable());
es.shutdown();
}
}
class MyRunnable implements Runnable{
public void run(){
for(int i = 0;i < 10;i++){
System.out.println(Thread.currentThread().getName()+"---"+i);
try{
Thread.sleep(300);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
3.创建一个可以缓存的线程池:
public class ThreadDemo{
public static void main(String[] args){
ExecutorService es = Executors.newCachedThreadPool();
es.execute(new MyRunnable());
es.execute(new MyRunnable());
es.shutdown();
}
}
class MyRunnable implements Runnable{
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("run--"+i);
try{
Thread.sleep(300);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
4.创建一个无限大小的线程池:
public class ThreadDemo{
public static void main(String[] args){
ScheduledExecutorService es = Executors.newScheduledThreadPool(3); //查询源码或百度
//ExecutorService es = Executors.newScheduledThreadPool(corePoolSize:3); 需要初始容量,简化如上
//es.execute(new MyRunnable());
//es.execute(new MyRunnable());
es.schedule(new MyRunnable(),delay:3000,TimeUnit.MILLISECONDS); //延时3秒开始创建
es.shutdown();
}
}
class MyRunnable implements Runnable{
public void run(){
for(int i = 0;i < 10;i++){
System.out.println("run--"+i);
try{
Thread.sleep(300);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}