java第五节 多线程/多线程的同步
/* 第五讲 多线程 了解进程和线程 在多任务系统中,每个独立执行的程序称为进程,也就是"正在进行的程序",我们现在使用的操作系统一般都是多任务的 即能够同时执行多个应用程序,实际情况是,操作系统负责对CPU等设备的资源进行分配和管理,虽然这些设备某一时时刻只能做一件事 但以非常小的时候间隔交替执行多个程序,就可以给人以同时执行多个程序的感觉o 一个进程中又可以包含一个或多个线程,一个线程就是一个程序内部的一条执行线索,如果要一个程序实现多段代码同时交替执行,就需产生多个线程,并指定每个线程上所要运行的程序代码段,这就是多线程 多线程与单线程的对比 ThreadDemo1.Main() -> TestThread.run(); 单线程 ThreadDemo1.Main() -> TestThread.run(); 多线程 用Thread类创建线程 1 要将一段代码在一个新的线程上运行,该代码应该在一个类的run函数中, 并且run函数所在的类是Thread类的子类,倒过来看,我们要实现多线程,必须编写一个继承了Thread类的子类 子类要覆盖Thread类中的run函数,在子类的run函数中调用想在新线程上运行的程序代码 2 启动一个新的线程,我们不是直接调用Thread子类对象中的run()方法,而是调用Thread子类对象中的start(从Thread类的继承到的)方法 Thread类对象的Start方法将产生一个新的线程,并在该线程上运行该Thread类对象中的run方法,根据面向对象运行时的多态性,在该线程上实际运行的是Thread子类(也就是我们写的那个类)对象中的run方法 3 由于线程的代码段在run方法中,那么该方法执行完成以后线程也就相应的结束了,因而我们可以通过控制run方法中循环的条件来控制线程的结束 后台线程与联合线程 如果我们对某个线程对象在启动(调用start方法)这前调用了setDaemon(true)方法,这个线程就变成了后台线程 对java程序来说,只要不家一个前台线程在运行,这个进程就不会结束,如果一个进程中只有后台线程在运行,这个进程就会结束 pp.join()的作用是把pp所对应的线程合并到调用pp.join(); 语句的线程中 使用Runnable接口创建多线程 1 适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效分离,较好的体现了面向对象的设计思想 2 可以避免由于java的单继承特性带来的局限,我们经常碰到这样的一种情况,即当我们要将已经继承了某一个类的子类放入多线程中 由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable的方式 3 当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例 4 事实上,几乎所有多线程应用都可用Runnable接口方式 多线程在实际中的应用 网络聊天程序的收发 1):如果一方从键盘上读取了数据并发送给了对方,程序运行到"读取对方回送的数据"并一直等持对访回送数据 如果对方没有回应,程序不能再做任何其他事情,这时程序处于阻塞状态,即使用户想正常终止程序运行都不可能 更不能实现"再给对方发送一条信息,催促对方赶快应答"这样的事情了 2):如果程序没有事先从键盘上读取数据并向外发送,程序将一直在"从键盘上读取数据"处阻塞,即使有数据从网上发送过来,程序无法到达"读取对方回送的数据"处,程序将不能收到别外先主动发送过来的数据 表记录的复制的中途取消 www服务器为每一个来访者都建立专线服务 while(bStop) { get; copy; } bStop = true; 多线程的同步 什么是线程安全 同步代码块 同步函数 代码块与函数间的同步 列锁问题 线程间的通信 放入新的数据前的状态 生产者准备放入的新数据 = = = = 张三 李四 男 女 生产者已放入 生产者还没有放入 放入后的状态(消费者最终取到的数据) 李四 男 wait:告诉当前线程放弃监视器并进入睡眠状态直到其他线程进入同一监视器并调用notify为止 notify: 唤醒同一对象监视器中调用wait的第一个线程,用于类似饭馆有一个空位后通知所有待候就餐的顾客中的第一位可以入座的情况 notify All:唤醒同一对象监视器中调用wait的所有线程,具有最高优先级的线程首先被唤醒并执行,用于类似某个不定期的培训班终于招生满额了,通知所有学员都来上课的情况 */ class ThreadDemo1 { public static void main(String[] args) { //new Thread().start(); //new TestThread().run(); //new TestThread().start(); //Thread tt = new TestThread(); //tt.setDaemon(true); //tt.start(); /*Thread tt = new Thread(new TestThread()); tt.start(); int index = 0; while(true) { if(index++ == 1000){ try{ tt.join(1000); }catch(Exception e){} } System.out.println("main() "+Thread.currentThread().getName()); }*/ /*new TestThread().start(); new TestThread().start(); new TestThread().start(); new TestThread().start();*/ /* TestThread tt = new TestThread(); tt.start(); tt.start(); tt.start(); tt.start();*/ TestThread tt = new TestThread(); new Thread(tt).start(); try{ Thread.sleep(1);}catch(Exception e){} tt.str = new String("method"); new Thread(tt).start(); //new Thread(tt).start(); //new Thread(tt).start(); } } //如果用多线程操作也有线程安全的问题 /*public void push(char c) { data[index] = c; index++; }*/ class TestThread implements Runnable //extends Thread { int tickets = 100; String str = new String(""); public void run() { //String str = new String(""); //不能将str对象放到run()代码块中,只能放到Thread类中才行 if(str.equals("method")) { while(true) { sale(); } }else{ while(true) { //同步语句块,在同一时间段只有一个线程应用该块代码 //synchronized()后面必须跟后一个任意的对象 //str => false true, 锁其标 synchronized(str){ //synchronized(this){ if(tickets > 0){ try{ Thread.sleep(10); }catch(Exception e){ } //死锁 //synchronized(this){} System.out.println("run() "+Thread.currentThread().getName()+" is saling ticket " + tickets--); } } } } } //代码块同步函数 //同步函数, 同步对象的标志问题 public synchronized void sale() { if(tickets > 0){ try{ Thread.sleep(10); }catch(Exception e){ } //死锁 //synchronized(str){} System.out.print("sale():"); System.out.println("sale run() "+Thread.currentThread().getName()+" is saling ticket " + tickets--); } } };
/* 线程的等待和唤醒过程 Thread t Synchronized(o) 线程t 得到对象0的lock旗标 o.wiat(); 此时线程t被放置到对象o的等待线程池中,t自动释放o的锁旗标 o.notify(); 当另外的线程执行了对象o的notify()方法后,线程t可能会被从o的等待线程池中释放出来 并且移动到等待线程对象o的锁旗标的线程池中,当t得到锁旗标时就会执行下去 线程生命的控制 程序中如何控制线程的生命 //suspend; */ class Product implements Runnable { Q q; public Product(Q q) { this.q = q; } public void run() { int i=0; while(true) { /*synchronized(this.q) { if(this.q.bFull){ try{this.q.wait();}catch(Exception e){}; } if(i == 0){ q.name = "zhangsan"; try{ Thread.sleep(1); }catch(Exception e){} q.sex = "male"; }else{ q.name = "lisi"; q.sex = "female"; } this.q.bFull = true; this.q.notify(); }*/ if(i==0){ this.q.put("zhangsan","male"); }else{ this.q.put("lisi","female"); } i = (i+1)%2; } } }; class Consumer implements Runnable { Q q; public Consumer(Q q) { this.q = q; } public void run() { while(true) { /*synchronized(this.q) { //wait(); if(!this.q.bFull) //如果缓存内容为空,退出进行睡眠 try{this.q.wait();}catch(Exception e){}; //System.out.println("Name:"+q.name+" Sex:"+q.sex); //this.q.get(); this.q.bFull = false; this.q.notify(); }*/ this.q.get(); } } }; class Q { private String name = "unknown"; private String sex = "unknown"; private boolean bFull = false; public synchronized void put(String name, String sex) { if(this.bFull){ try{ this.wait(); }catch(Exception e){} } this.name =name; try{ Thread.sleep(10); }catch(Exception e){} this.sex = sex; this.bFull = true; this.notify(); } public synchronized void get() { if(!this.bFull){ try{ this.wait(); }catch(Exception e){} } System.out.println("Name:"+this.name+" Sex:"+this.sex); this.bFull = false; this.notify(); } }; class ThreadCommunation { public static void main(String []args) { //Q q = new Q(); //new Thread(new Product(q)).start(); //new Thread(new Consumer(q)).start(); ThreadTest tt = new ThreadTest(); new Thread(tt).start(); for(int i=0; i<100; i++) { if(i==50){ tt.stopMe(); } System.out.println("main() is running i="+i); } } } class ThreadTest implements Runnable { private boolean bStop = true; public void stopMe() { this.bStop = false; } public void run() { while(bStop) { System.out.println("run "+Thread.currentThread().getName()+" is running"); } } };