关于Java中的线程 --------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
1. 进程 进程:正在运行的程序,所占有内存空间 程序存储在硬盘,运行时期到了内存中 线程:是一个大程序中的子程序 CPU真正执行的是线程,子程序对于CPU来讲独立执行路径,路径就是线程//======================================================2. Java中创建线程 任何都是对象,线程也是对象,对象的描述类 java.lang.Thread类 创建线程的第一种方式,继承Thread类,重写run方法 创建Thread子类对象 调用子类中的方法start()开启线程 void start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 JVM 本身是单线程的程序,还是多线程的程序 一个线程运行我们写的程序,一个线程不定时收取垃圾,JVM帮助你调用Windows中的功能 为什么继承Thread类,入伙,继承Thread类,子类就是一个线程了 为什么重写run方法 Thread中的run方法是空方法,等待子类重写 线程运行的程序是什么,未知的,Java工程师,开放多线程技术的时候 不知道使用Java编程的人员会运行哪些代码 提供一个标准,就是run方法:不管线程运行哪些代码,必须放在run中,线程就运行你run中的代码
/* * 创建线程第一种方式,继承Thread类 */class Demo1 extends Thread{ public void run(){ for(int x = 0 ; x < 50 ;x++){ System.out.println("run..."+x); } }}public class ThreadDemo1 { public static void main(String[] args) { while(true){ Demo1 d = new Demo1(); d.start();//开启线程,JVM自动调用线程的run方法 for(int x = 0 ; x < 50 ; x++){ System.out.println("main..."+x); } } }}
//======================================================
4. 线程的状态图,必须自己会画
5. 线程名字的获取和设置 获取名字,Thread类的方法getName(),返回字符串 线程名字JVM命名的 Thread-0 Thread-1 在Thread子类中,直接使用父类方法getName()获取名字 在不是Thread子类中,获取线程名字 Thread类中,提供了一个静态方法 currentThread()返回值是一个Thread类的对象 方法,返回当前线程对象,既然返回的是对象,方法调用链 String name = Thread.currentThread().getName(); 设置线程的名字: Thread类的方法setName() Thread类的构造方法 Thread(String name)传递线程名字 Thread子类中,super语句将线程的名字送到父类构造方法
class ThreadName extends Thread{ ThreadName(String name){ super(name); } public void run(){ String name =Thread.currentThread().getName(); System.out.println(name+" 线程ThreadName"); }}public class ThreadDemo { public static void main(String[] args) { //Thread.currentThread();//返回的就是运行main方法的线程对象 String name = Thread.currentThread().getName(); System.out.println(name); ThreadName t1 = new ThreadName("西班牙0"); ThreadName t2 = new ThreadName("智利2"); // t1.setName("小强"); // t2.setName("旺财"); t1.start(); t2.start(); }}
//======================================================6. 售票的一个案例 利用多线程模拟多窗口一起售票 模拟出了,卡机线程,导致了数据的安全问题 多线程操作同一个数据的时候,出现数据安全问题 解决办法:一个线程不执行完毕,其他的线程,不能执行 Java中开放出了同步技术,保证线程的安全性,会阻止其他线程进入 同步代码块 synchronized(任意对象){ 线程操作的共享数据 }
/* * 模拟售票4个窗口一起出售 * 改造成实现Runnable接口的方式 */class Ticket implements Runnable{ private int tickets = 100; private Object obj = new Object(); public void run(){ while(true){ synchronized(obj){ if(tickets > 0){ //线程if判断完毕后,休眠一段时间 try{ Thread.sleep(67); }catch(Exception e){} System.out.println(Thread.currentThread().getName()+"..出售第.."+tickets--); } } } }}public class ThreadDemo1 { public static void main(String[] args) { //创建Ticket对象 Ticket t = new Ticket(); //创建Thread类对象,传递Runnable接口的实现类对象 Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); t1.start(); t2.start(); t3.start(); t4.start(); }}
//======================================================7. 创建线程的第二种方式 定义类,实现Runnable接口 重写run方法 class A implements Runnable{ public void run(){ } } A类不再是线程类了 直接创建Thread类对象 构造方法Thread(Runnable target) 接受的数据类型是Runnable接口的子类类型 new Thread(new A()); 调用Thread类中的start();
两个线程的创建方式的区别 继承Thread类,实现Runnable接口区别 继承,单继承局限性 接口,多现实,避免了单继承的局限性 继承Thread类方式,线程中的数据是对象自己的 实现接口方法,线程中的数据是共享的 写多线程程序推荐使用接口方式//======================================================8. 同步的原理 synchronized(任意对象){ 线程操作的共享数据 } 对象,写在了同步中 专业名词,对象监视器 通俗一点:锁 线程进到同步代码块后,线程获取这把锁,这扇门永久关闭了 当线程出去同步代码块,将锁释放 厕所原理 多线程操作同一个数据,安全问题 如果是单线程,没有数据安全问题
//======================================================9. 模拟存钱 一张卡,可以多个柜台存钱 余额是0,每个柜台每次存100元 两个柜台,每个存3次 600元,没存一次,查看余额 同步方法,在方法的声明上写同步关键字 线程每次只有一个运行这个方法 当方法中所有代码都是线程操作的的共享数据 同步方法中,锁是什么 确定的是,锁肯定有,锁必须是对象 锁是本类的对象引用this 方法中的同步代码块,锁直接写this 静态方法中的,同步锁是谁 锁是对象! 静态优先于对象,静态中的锁,是本类的class文件对象 Java中,每一个数据类型,JVM都赋予他们一个静态成员变量 class名字,变量的运行结果就是类的class文件对象 静态方法中的锁,就是本类.class字节码文件对象
/* * 存钱的时候 * 卡,钱,到银行中去,银行柜台存钱 * 但是,整个Add方法中的所有代码,都是线程操作的共享数据 * 没有必要同步一段代码,同步整个方法 */class Bank{ //存钱功能,存一次,看余额 private static int sum = 0; public static synchronized void add(int money){ // synchronized(Bank.class){ sum = sum + money; System.out.println(sum); // } }}class Customer implements Runnable{ private Bank b = new Bank(); public void run(){ for(int x = 0 ; x < 3 ; x++){ Bank.add(100); } }}public class ThreadDemo2 { public static void main(String[] args) { Customer c = new Customer(); Thread t1 = new Thread(c); Thread t2 = new Thread(c); t1.start(); t2.start(); }}
//==============================================================10. 单例模式 懒汉有安全隐患,多线程并发访问懒汉式的时候 安全问题,同步避免 效率很低,提高懒汉的效率 第一次执行s=null 进同步 创建对象,返回 第二次执行s!=null 没有必要进同步了,直接返回 两次判断,提高效率
class Single{ private Single(){} private static Single s = null; public static Single getInstance(){ if(s == null){ synchronized(Single.class){ if( s == null) s = new Single(); } } return s; }}class SingleThead implements Runnable{ public void run(){ for(int x = 0 ; x < 30 ; x++){ Single s = Single.getInstance(); System.out.println(s); } }}
//==============================================================11. 死锁案例 在多线程的技术中,两个或者两个以上的线程,同时争夺一个对象监视器 导致程序的假死现象 死锁:出现条件,必须多线程,争夺一个锁,程序中,体现在同步代码块的嵌套效果 死锁的案例,面试过程中经常被考到//==============================================================12. 线程的通信 两个线程同时对一个资源对象,进行赋值和取值操作 思考,数据安全问题怎么发生的 发生后,怎么解决 数据安全问题:线程的随机性导致程序数据安全隐患 采用了同步技术,依然没有解决 当你发现数据安全问题后,使用了同步技术,还是不能解决 第一点,确定程序是不是多线程在操作共享数据 确定 第二点,使用的同步中的锁,是同一把锁吗,锁不同唯一的 必须将锁变成唯一的对象,才能控制数据安全问题 资源对象,数据的安全性解决了
线程等待和唤醒的方法 没有出现在线程描述类Thread类中 方法定义在了Object类中,为什么这样设计 原因是锁,锁是一个对象,对象是通过类new出来的 锁是哪一个类的对象,无法确定的 但是将方法写在Object类中,所有的类的对象,都具有线程的等待与唤醒方法 wait()方法的使用,将线程永久等待,直到有人唤醒 wait方法必须有锁的支持,wait方法必须写在同步中 锁是唯一的 synchronized(r){ wait(); notify(); } synchronized(r){ notify() } IllegalMonitorStateException 异常,运行时期的异常,抛出该异常,说明wait notify没有锁的支持,没有对象监视器
/* * 线程通信的代码的优化 *///定义资源类,私有处理class Recource{ private String name; private String sex; private boolean flag = false; //提供get set方法,访问成员变量 public synchronized void set(String name,String sex){ if(flag) try{ this.wait(); }catch(Exception e){} this.name = name; this.sex = sex; flag = true; this.notify(); } public synchronized void get(){ if(!flag) try{ this.wait(); }catch(Exception e){} System.out.println(name+"..."+sex); flag = false; this.notify(); }}//输入线程class Input implements Runnable{ private Recource r; Input(Recource r){this.r = r;} public void run(){ int x = 0 ; while(true){ if(x %2 == 0){ r.set("张三", "男"); }else{ r.set("李四", "女"); } x++; } }}//输出的线程class Output implements Runnable{ private Recource r ; Output(Recource r){this.r=r;} public void run(){ while(true){ r.get(); } }}public class ThreadDemo5 { public static void main(String[] args) { Recource r = new Recource(); new Thread(new Input(r)).start(); new Thread(new Output(r)).start(); }}
//==============================================================13. wait() sleep() 导致线程等待,两个方法区别在哪里(不要光看表面。) sleep(毫秒值)自动醒来 wait()永久等待,需要别的线程唤醒
sleep()方法是Thread类的静态方法 wait()方法是Object类的非静态方法
sleep()不需要对象锁 wait()必须有锁的支持
sleep()方法,执行的时候线程不会释放对象锁 wait()方法,执行的时候,线程放弃对象锁,被唤醒的时候,从新获取对象锁,才能运行//==============================================================14. 定时器 Java程序中,有定时功能,按照一定的时间间隔运行指定的程序 定时器类,java.util.Timer 构造方法 Timer(boolean isDaemon) false 不是守护的线程 schedule(TimerTask task, Date firstTime, long period) 定时器任务 第一次时间 间隔 抽象类 TimerTask 时间任务,定时器执行的程序,写在这个类的run方法
/*class Time extends TimerTask{ public void run(){ System.out.println("定时2秒钟发送一次邮件"); }}*/public class TimerDemo { public static void main(String[] args) { Timer t = new Timer(false); t.schedule(new TimerTask(){ public void run(){ System.out.println("\"定时2秒钟发送一次邮件\""); } }, new Date(), 3600000); System.out.println("main...over"); }}
//==============================================================
15. 多线程通信,生产者与消费者 一个产品,分别线程控制,一个控制生产,一个控制消费 生产一个,消费一个,多生产者,多消费者 多个生产与多个消费,全部的唤醒notifyAll() 唤醒以后,数据安全性还是没解决 线程在wait上等待,被唤醒的时候,从Wait这里起来 起来以后,不会再次判断flag是true,还是false,因此数据问题,没哟解决 线程,被唤醒以后,但是判断标记!! 用的是循环的方式,解决线程倒下去后,再起来,必须还要判断标记 但是发现一个问题: notifyAll()唤醒了全部等待的线程 1个活的,7个等着,全部醒来 浪费资源,能不能唤醒对方的一个线程的 生产者,只唤醒一个消费者 消费者,只唤醒一个生产者 唤醒本方是没有意义,全部唤醒是浪费资源的 Java的JDK1.4版本之前,是做不到了 到了JDK1.5版本后,就可以实现了 提供一套新的多线程的操作方式 synchronized,被替换了 wait(),notify(),notifyAll(),被替换了 JDK1.5的新特性,线程锁定操作 导包 java.util.concurrent.locks
//==============================================================16. JDK1.5的锁 lock接口 Lock接口--synchronized同步 同步是有锁提供 接口中,定义了两个方法 lock() unlock() 要同步线程的时候,使用这两个方法,进行锁操作 Lcok接口的实现类对象ReentrantLock 获取到接口的实现类对象,调用方法,锁操作 新的技术中,JDK提供了一个接口Condition 替换了原有线程方法,wait,notify 将线程进行分组管理 t1-t4 set方法,用锁就是set方法中的锁 t5-t8 get方法,用锁就是get方法中的锁 一个lock接口上,可以实现多个Condition对象 final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); 将一个接口Lock,分成两组管理 Condition接口中的三个方法 await() -- wait() signal() -- notify() signalAll() -- notifyAll();
/* * 将案例,改造成lock锁方式实现功能 */import java.util.concurrent.locks.*;class Product{ private String name; //定义计数器 private int count ; //定义标识 private boolean flag = false; //定义Lock锁对象 private Lock lock = new ReentrantLock(); //通过Lock接口,获取Condition对象 private Condition pro = lock.newCondition(); private Condition cus = lock.newCondition(); //定义生产方法 public void set(String name){ //获取锁 lock.lock(); while(flag) try{ pro.await(); }catch(Exception e){} this.name = name + count++; System.out.println(Thread.currentThread().getName()+"生产第..."+this.name); flag = true; //this.notifyAll(); //释放锁 cus.signal(); lock.unlock(); } //定义消费方法 public void get(){ lock.lock(); while(!flag) try{ cus.await(); }catch(Exception e){} System.out.println(Thread.currentThread().getName()+"消费第........."+this.name); flag = false; //this.notifyAll(); pro.signal(); lock.unlock(); }}//定义生产者class Producter implements Runnable{ private Product p ; Producter(Product p){this.p = p;} public void run(){ while(true){ p.set("键盘"); } }}//定义消费这class Customer implements Runnable{ private Product p ; Customer(Product p){this.p = p;} public void run(){ while(true){ p.get(); } }}public class ThreadDemo { public static void main(String[] args) { Product p = new Product(); Producter producter = new Producter(p); Customer customer = new Customer(p); Thread t1 = new Thread(producter); Thread t2 = new Thread(producter); Thread t3 = new Thread(producter); Thread t4 = new Thread(producter); Thread t5 = new Thread(customer); Thread t6 = new Thread(customer); Thread t7 = new Thread(customer); Thread t8 = new Thread(customer); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); t6.start(); t7.start(); t8.start(); }}
//==========================================================17. 线程的停止方式 Thread类中,有一个方法stop(),过时
终止线程的运行,目的结束run方法就行 停止线程的第一种方式,改变变量的值,结束while循环,结束了run方法 处于等待中的线程,怎么停下 例子: 我有一个朋友,失眠,找了一个催眠大师(水平很高) 进行了催眠,朋友就睡了(wait()) 催眠师说,被我催眠的人,只有我能叫醒 催眠师死了,不能让朋友永久的等待下去 拍你一板砖,醒来,收到了伤害(异常) 线程的第二种停止方式 void interrupt() + 异常停下,等待中的线程 打击线程方法,处于等待的线程,将会抛出异常
/* * 线程如何停止下来 */class StopThread implements Runnable{ private boolean flag = true; public void run(){ while(flag){ synchronized(this){ try{this.wait();}catch(Exception e){ e.printStackTrace(); //System.out.println(e.getMessage()+"打你一板砖"); flag = false; } System.out.println("run...."); } } } public void setFlag(boolean flag){ this.flag = flag; }}public class ThreadDemo1 { public static void main(String[] args) { StopThread st = new StopThread(); Thread t = new Thread(st); t.start(); for(int x = 0 ; x < 1000 ; x++){ if(x==999) //st.setFlag(false); t.interrupt(); else System.out.println("main"+x); } }}
//==========================================================18. 守护线程 Thread类中。setDaemon(boolean )传递是true,线程守护线程 如果所有运行的线程,都是守护线程,JVM退出
方法,必须在start开始前调用
Feiq,开启多个聊天窗口的时候,一旦关闭飞秋主程序,聊天窗口也就关闭了 聊天窗口线程,就是飞秋主线程的守护线程,一旦主线程死亡,所有的守护线程就死亡
/* * 守护线程 */class ThreadDaemon implements Runnable{ public void run(){ while(true) System.out.println("run...."); }}public class ThreadDemo2 { public static void main(String[] args) { ThreadDaemon td = new ThreadDaemon(); Thread t = new Thread(td); t.setDaemon(true); t.start(); }}
//==========================================================19. Thread中其他方法,toString() setPriority() join() yield() toString()继承Object,重写toString() Thread[Thread-0,5,main] 5 优先级,main 线程组 优先级三个级别,最低,默认,最高 setPriority(int )设置优先级 但是优先级的效果,多核,多线程的CPU上,效果不是很明显了 join() 加入 等待该线程终止 使用join方法的线程,不停止,其他线程运行不了 static yield()
/* * 线程的让步方法,static yield */class YieldThread implements Runnable{ public void run(){ for(int x = 0 ; x < 100 ; x++){ Thread.yield(); System.out.println(Thread.currentThread().getName()+"run.."+x); } }}public class ThreadDemo5 { public static void main(String[] args) { YieldThread yt = new YieldThread(); Thread t0 = new Thread(yt); Thread t1 = new Thread(yt); t0.start(); t1.start(); }}
/* * 等待该线程终止 */class JoinThread implements Runnable{ public void run(){ for(int x = 0 ; x < 100; x++){ System.out.println(Thread.currentThread().getName()+"run.."+x); } }}public class ThreadDemo4 { public static void main(String[] args) throws Exception{ JoinThread jt = new JoinThread(); Thread t0 = new Thread(jt); Thread t1 = new Thread(jt); t0.start(); t0.join(); t1.start(); for(int x = 0 ;x < 100 ; x++){ System.out.println("main...."+x); } }}
/* * Thread中的toString() */class ThreadToString implements Runnable{ public void run(){ for(int x = 0 ; x < 100 ; x++){ System.out.println(Thread.currentThread().getName()+"run.."+x); } }}public class ThreadDemo3 { public static void main(String[] args) { ThreadToString tts = new ThreadToString(); Thread t0 = new Thread(tts); Thread t1 = new Thread(tts); t0.setPriority(Thread.MAX_PRIORITY); t1.setPriority(Thread.NORM_PRIORITY); t0.start(); t1.start(); // System.out.println(t); }}
--------- android培训、java培训、java学习型技术博客、期待与您交流! ------------