一、概念及理解
|--进程:正在进行中的程序(直译)。|--线程:进程中一个负责程序执行的控制单元(执行路径)。
1、一个进程中可以有多个执行路径,称之为多线程。2、一个进程中至少要有一个线程。 3、开启多个线程是为了同时运行多部分代码,每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。
|--多线程相对单线程的特点
|--多线程的好处:解决了多部分代码同时运行的问题。 |--多线程的弊端:线程太多,会导致效率的降低。
其实,多个应用程序同时执行都是CPU在做着快速的切换完成的。这个切换是随机的。CPU的切换是需要花费时间的,从而导致了效率的降低。 JVM启动时启动了多条线程,至少有两个线程可以分析的出来: 1.执行main函数的线程,该线程的任务代码都定义在main函数中。 2.负责垃圾回收的线程。二、线程创建方法
|--继承Thread类:直接继承Thread类,必需实现run()方法;
Thead_Demo t = new Thread_Demo(); t.start();
|--实现Runnable接口:因为java是单继承,使用接口增强了代码的扩展性,必需实现run()方法,一般用此方法:
Runnable_Demo rd = new Runnable_Demo(); Thread t = new Thread(rd); r.start();
|--卖票举例
package threadTest; /** *买票线程的思考: *1.一个资源,多个线程执行,票和买票窗口是不同类别,分别建立Ticket类、TicWindow类; *2.由于是多个线程,所以要考虑到安全问题,用同步机制; *3.一个资源,多个操作,属于资源共享情况,有两种方法可以解决,这里用静态方法;(还有一种可以传递统一对象) * */ public class TicketTest { public static void main(String[] args) { for (int i = 1; i < 4; i++) { new Thread(new TicWindow(String.valueOf(i))).start(); } } } //定义票的资源,要用到的资源及方法定义成静态 class Ticket{ private static int tic = 100; public static int sellTic(){//卖票的方法 return tic--; } public static int getTic(){ return tic; } public static void setTic(int tic) {//设置多少张票 Ticket.tic = tic; } } //定义买票窗口类, class TicWindow implements Runnable { String name; public void run(){ while(true){ //同步代码块保证线程安全 synchronized(TicWindow.class){ //判断是否有票,有则买,没有就退出线程 if(Ticket.getTic()>0) try { Thread.sleep(50); System.out.println(Thread.currentThread().getName()+"第"+name+"窗口"+"卖出第 "+Ticket.sellTic()+" 张票"); } catch (InterruptedException e) { e.printStackTrace(); } else break; } } } TicWindow(String name){ this.name=name; } }
三、线程状态及线程常用方法
|--线程状态
|--运行 start()|--冻结 wait()和sleep(),放弃了CPU的执行权 |--等待 有cpu的执行权,但还没执行 |--停止 run()方法结束,通过建立“生命周期标记”来控制run循环结束
|--线程常用方法
|--interrupt() 清除中断状态(wait()/sleep()/join()) |--join() 暂停其它线程,执行指定线程 |--yield() 暂停当前正在执行的线程对象,并执行其他线程。 |--setPriority() 更改线程的优先级(一般用0、5、10级) |--setDaemon() 指定为守护线程,当所有线程都为守护线程时,程序停止 |--currentThread() 返回对当前正在执行的线程对象的引用 |--toString() 返回该线程的字符串表示形式,包括线程名称、优先级和线程组。
四、线程的安全(同步机制)
|--同步的两种方法:
|--Lock、ConditionJDK1.5升级后的新特性,Lock取代了 synchronized,Condition取代了wait/notify/notifyAll,让程序等待唤醒机制更灵活
|--synchronized
|--synchronized(obj){} 代码块
|--好处:解决了线程的安全问题|--缺点:判断锁会耗费资源,线程较多时,影响程序运行效率 |--前提:需要有两个以上线程才上锁
|--synchronized函数:锁对象默认是this对象;如果是静态函数,锁对象默认是 类名.class
|--同步注意的两个点
|--死锁:两个不同的锁相嵌套,相互关联,相互不放锁,使线程停止运行的情况,用于程序的同步|--同锁:针对线程之间的通信,必需用相同的锁
五、等待唤醒机制
等待唤醒机制其实是通过一个标记,合理运用wait() 和notify()/notifyAll()方法控制线程,最终达到控制CPU执行顺序的目的。等待的线程都存在于线程池中。notify()唤醒第一个线程,notifyAll()唤醒全部。
|--方法一 synchronized wait()和notify()定义在上帝类Object中的原因是,任意对象都可以作当作锁的对象(监视器),根据面向对象的向上转型的思维,定义上帝类Object类是通用的……只有同一个锁上的被等待线程,可以被同一个锁上notify()唤醒
|--方法二 Lock、ConditionLock类要手动加锁和关锁,一个锁上可以设置多个监视器
六、多线程之间的通信
|--思路 : 针对共同资源进行操作,一般模式:class Res{ //to do something } class T1 implements Runnable{ Res r; T1(Res r){ this.r =r; } public void run{} } class T2 implements Runnable{ Res r; T2(Res r){ this.r =r; } public void run{} } //然后在main方法中对同一对象操作 public static void main(String[] args ){ Res r = new Res(); T1 t1 = new T1(r); T2 t2 = new T2(r); }
|--线程通信的安全不同线程之间用同一锁,否则出错。可以统一用资源对象。package threadTest; /** *多线程之间通信考虑的问题: *1.资源共享:两种方法,static静态处理,IO流装饰类模式; *2.线程安全:synchronized,Lock,这里运用synchronized; *3.线程运行次序,即等待唤醒机制:运用wait()/notify(); *注意: *1.wait()抛出InterruptedException,需要处理; *2.wait()/notify()是定义在“上帝类中”,同一锁调用,如 p.wait(); *步骤: *1.建立资源类,操作类1、操作类2、操作类3…………操作类中new资源对象,并让对象一致; *2.多线程考虑安全问题,用synchronized函数; *3.主函数中建立对象,开启线程; * */ public class InputOutputDemo { public static void main(String[] args) { Person p = new Person(); Input in = new Input(p); Output out = new Output(p); new Thread(in).start(); new Thread(out).start(); //以上四句,可以简写成以下 //new Thread(new Input(p)).start(); //new Thread(new Output(p)).start(); } } //1.定义资源类Person class Person{ private String name ; private String sex ; private boolean flag = false;//用于控制等待与唤醒的标记 public synchronized void set (String name ,String sex){ if(this.flag){ try {this.wait();}catch (InterruptedException e) { e.printStackTrace();} } this.name= name; this.sex = sex; this.flag = true; this.notify(); } public synchronized void out(){ if(!this.flag){ try {this.wait();}catch (InterruptedException e) { e.printStackTrace();} } System.out.println(this.name+"…………"+this.sex); this.flag = false; this.notify(); } } //2.定义输入类Input class Input implements Runnable{ //定义资源对象,类似于IO流的装饰类 Person p; int x =0; public void run() { while (true) { if (x == 0) p.set("mike", "man"); else p.set("丽丽", "女女女女女"); x = (x + 1) % 2; } } Input(Person p){ this.p = p; } } //3.定义输出类Output类 class Output implements Runnable{ Person p ; public void run() { while(true){ p.out(); } } Output(Person p){ this.p = p; } }
|--等待唤醒机制两个方法(synchronezied、lock),考虑到效率问题一般用lock/Condition方法,lock/Conditions可以更加具体控制、更加针对控制同一类操作线程。package threadTest; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; /** *多线程间通信测试:多个生产者同时生产,多个消费都同时消费 *思考:多线程通信目前有两种方法: *1.static方法, *2.类似于IO流的装饰模式; *ps.static在内存中存在周期过长,对内存不友好,一般用装饰模式,即建立资源统一操作 *步骤: *1.建立资源类,操作类1、操作类2、操作类3…………操作类中new资源对象,并让对象一致; *2.多线程考虑安全问题,等待唤醒优先用Lock/Condition方法,不同操作类用不同锁; *3.主函数中建立对象,开启线程; * */ public class ProducerConsumerDemo { public static void main(String[] args) { Resource r = new Resource(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); } } //1.创建资源类Resource class Resource{ private int count=0; private String name ; private boolean flag=false; private Lock lock = new ReentrantLock();//父类引用指向子类对象 private Condition condition_pro = lock.newCondition();//生产监视 private Condition condition_con = lock.newCondition();//消费监视 //生产方式,用Lock/Condition方法保证线程同步及等待唤醒 public void set(String name){ lock.lock();//关锁 try { while(flag) condition_pro.await(); this.name = name; this.count ++; Thread.sleep(50); System.out.println(Thread.currentThread().getName()+"……生产………"+this.name+this.count); flag = true; condition_con.signalAll(); } catch (Exception e) { e.printStackTrace(); } finally{ lock.unlock();//开锁,关闭资源的语句定义在finally中 } } //消费方式,用Lock/Condition方法保证线程同步及等待唤醒 public void out(){ lock.lock();//关锁 try { while(!flag) condition_con.await();//消费都等待 Thread.sleep(50); System.out.println(Thread.currentThread().getName()+"……消费…………………"+this.name+this.count); flag = false; condition_pro.signalAll();//唤醒生产者的线程 } catch (Exception e) { e.printStackTrace(); } finally{ lock.unlock();//开锁,关闭资源的语句定义在finally中 } } } //2.创建生产者Producer class Producer implements Runnable{ Resource r; public void run(){ while(true){ r.set("箱子"); } } Producer(Resource r){ this.r = r; } } //创建消费者Consumer class Consumer implements Runnable{ Resource r; public void run(){ while(true){ r.out(); } } Consumer(Resource r){ this.r = r; } }