毕向东之线程
/*进程(如任务管理器):是一个正在执行中的程序,每一个进程执行都有一个执行顺序。 线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行。并且一个进程中至少有一个线程。 Java VM 启动的时候会有一个进程java.exe.该进程中至少有一个线程负责java程序的执行 而且这个线程运行的代码存在于main方法中。该线程称为主线程 扩展:如果更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程 创建线程的第一种方式:继承Thread类 步骤:1.定义类继承Thread 2.复写Thread类中的run方法 (目的:将自定义代码存储在run方法中,让线程运行) 3.调用线程的static方法(该方法作用:启动线程,调用run方法) 发现运行结果每一次都不同。因为多个线程都获取cpu的执行权,cpu执行到谁,就运行谁。在某一个时刻,只能有一个 程序在运行(多核除外) cpu在做着快速的切换,以达到看上去是同时运行的效果。 多线程的特性:随机性,随抢到执行权就执行谁 */ public class Test1 { public static void main(String[] args) { Demo d=new Demo();//创建线程 d.start();//开启线程 执行run方法 //d.run();这样仅仅是对象调用方法。线程创建了,并没有运行 for(int x=0;x<60;x++){ System.out.println("====主线程"+x); } } } class Demo extends Thread{ public void run(){ for(int x=0;x<60;x++){ System.out.println("线程"+x); } } }
/*1.线程创建 2.运行(既有运行权又有运行资格):start(); 3.消亡:1).stop(); 2).run方法结束 3.冻结(放弃了执行资格):包括sleep(time);wait();notify()唤醒(唤醒后有运行资格,不一定有执行权) 线程都有自己的名称,默认为:Thread-编号。编号从0开始 调用父类的构造方法就可以改变默认的名称 Thread类的静态方法: currentThread();获取当前线程对象 getName(); 获取线程名称。 设置线程名称:setName或者构造函数 */ public class Test2 { public static void main(String[] args) { Test t1=new Test("one---"); Test t2=new Test("two+++"); t1.start(); t2.start(); for(int x=0;x<10;x++){ System.out.println("=====主线程:"+x); }//三个线程都执行完程序结束 } } class Test extends Thread{ public Test(String name){ super(name);//将自定义name传给父类的构造方法 } public void run(){ for(int x=0;x<10;x++){ System.out.println(Thread.currentThread().getName()+"==:"+x); } } }
/*创建线程的第二个方法: 步骤:1.定义类实现Runnable接口。(避免了单继承的局限性) 2.覆盖Runnable中的Run方法(将线程需要执行的代码放run方法中) 3.通过Thread类建立线程对象 4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。 5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法 两种方式的区别: 继承Thread:线程代码存放在Thread子类run方法中。 实现Runnable:线程代码存在接口的子类的run方法中。 多线程的运行出现安全问题。 问题的原因:当多条语句在操作同一个线程共享时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。 解决办法:对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与进来。 java对于多线程的安全问题提供了专业的解决方式:就是同步代码块: synchronized(对象){ 需要同步的代码块; } 对象如同锁。持有锁的线程可以在同步中执行,没有执行锁的线程即使获取了cpu的执行权,也进不去,因为没有获取锁。 同步的前提: 1.必须要有两个或者两个以上的线程 2.必须是多个线程使用同一个锁 必须保证同步中只能有一个线程在运行。 好处:解决了多线程的安全问题。 弊端:多个线程需要判断锁,较为消耗资源, */ public class Test3 { public static void main(String[] args) { Demo3 d=new Demo3(); Thread d1=new Thread(d);//创建线程,执行d的run方法 Thread d2=new Thread(d); d1.start(); d2.start();//两个线程都实现d的run方法 } } class Demo3 implements Runnable{ private static int tick=100; Object obj=new Object(); public void run(){ while(true){ synchronized(obj){//同步里面一对象锁 if(tick>0){//当在这里判断后执行权被抢走时,就会出现数据的错乱。就需要用同步来解决 try{ Thread.sleep(10); }catch(Exception e){}//睡眠等待异常 System.out.println(Thread.currentThread().getName()+"sale:"+tick--);//当前对象的getName(); } } } } }
/*两个线程运行同一个对象的run方法 同步函数使用的锁是this 如果同步函数被静态修饰后,使用的锁是类名。类名.class 因为静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件的对象(类名.class) */ public class Test4 { public static void main(String[] args) { Cus c=new Cus(); Thread t1=new Thread(c); Thread t2=new Thread(c); t1.start(); t2.start();//都运行c的run方法 } } class Bank{ private int sum; //Object obj=new Object(); public synchronized void add(int n){//同步函数 //synchronized (obj){ 同步代码块 sum=sum+n; try{ Thread.sleep(10); }catch(Exception e){} System.out.println(Thread.currentThread().getName()+"sum="+sum); //} } } class Cus implements Runnable{ private Bank b=new Bank(); public void run(){ //实现父类 不能抛 for(int x=0;x<3;x++){ b.add(100); } } }
/* 对于多个生产者与消费者,为什么要定义while判断标记? 原因:让被唤醒的线程再次判断标记。 为什么要定义notifyAll? 因为需要唤醒对方线程,如果只用notify,容易出现只唤醒本方线程的情况,导致程序中的所有线程都等待。 */ /*========================================================================== public class Test5 { public static void main(String[] args) { Resource r=new Resource(); Producer pro=new Producer(r); Consumer con=new Consumer(r); Thread t1=new Thread(pro); Thread t2=new Thread(pro); Thread t3=new Thread(con); Thread t4=new Thread(con); t1.start(); t2.start(); t3.start(); t4.start(); } } class Resource{ private String name; private int count=1; private boolean flag=false; public synchronized void set(String name){ while(flag)//因为被等待的线程醒过来后,会继续往下执行代码,如果此处为if,就不会判断flag标记,造成数据出错 try{wait();}catch(Exception e){ } this.name=name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag=true; this.notifyAll(); } public synchronized void out(){ while(!flag) try{wait();}catch(Exception e){ } System.out.println(Thread.currentThread().getName()+"....消费者......."+this.name); flag=false; this.notifyAll(); } } class Producer implements Runnable{ private Resource res; public Producer(Resource res){ this.res=res; } public void run(){ while(true){ res.set("+商品+"); } } } class Consumer implements Runnable{ private Resource res; public Consumer(Resource res){ this.res=res; } public void run(){ while(true){ res.out(); } } } =======================================================================================*/
/* jdk5.0后:Lock替代了synchronized, Condition替代了Object 就避免了while,notifyAll的麻烦 */ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class Test6 { public static void main(String[] args) { Resource r=new Resource(); Producer pro=new Producer(r); Consumer con=new Consumer(r); Thread t1=new Thread(pro); Thread t2=new Thread(pro); Thread t3=new Thread(con); Thread t4=new Thread(con); t1.start(); t2.start(); t3.start(); t4.start(); } } /*如果t3、t4抢到执行权,遇到flag则等待,假设t1先抢到执行权:生产一个,改变标记,唤醒对方并释放锁;如果t1或者t2抢到执行权则等待; 再假设t3抢到执行权,得到锁,则消费一个,改变标签,唤醒对方并释放锁;如果t3或者t4抢到执行权则等待;被唤醒的t1或t2中的一个则生产一个.... 同步函数与lock的区别:当线程被等待后,该函数的功能则结束,同步也就结束,但得到lock后,被等待函数也结束,但是没释放锁,所以释放锁必须得放到finally里 */ class Resource{ private String name; private int count=1; private boolean flag=false; private Lock lock=new ReentrantLock();//创建一锁 并且一个锁可以绑定好几个对象 private Condition condition_pro=lock.newCondition();//返回绑定到Lock实例的新Condition实例 private Condition condition_con=lock.newCondition();//不同对象的锁 public void set(String name){ lock.lock();//得到锁 try{ while(flag)//因为被等待的线程醒过来后,会继续往下执行代码,如果此处为if,就不会判断flag标记,造成数据出错 condition_pro.await();//生产者等待 this.name=name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag=true; condition_con.signal();//唤醒某一个消费者 实现了本方只唤醒对方操作 }catch(Exception e){ } finally{ lock.unlock();//释放锁 必须被执行 } } public synchronized void out(){ lock.lock(); try{ while(!flag) condition_con.await(); System.out.println(Thread.currentThread().getName()+"....消费者......."+this.name); flag=false; condition_pro.signal();//避免唤醒了本方线程 }catch(Exception e){ } finally{ lock.unlock();//释放锁 } } } class Producer implements Runnable{ private Resource res; public Producer(Resource res){ this.res=res; } public void run(){ while(true){//不搞循环就没必要多线程,但要避免死循环(下例为解决方法) res.set("+商品+"); } } } class Consumer implements Runnable{ private Resource res; public Consumer(Resource res){ this.res=res; } public void run(){ while(true){ res.out(); } } }
/*stop方法已经过时,如何停止线程呢?只有一种方法,让run方法结束 开启多线程运行,运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束 特殊情况:当线程在执行改变标记之前时被冻结了,线程就不会改变标记,就不会结束线程。所以就有了interrupt(会产生中断异常)。 interrupt就是强制将冻结的线程恢复到运行状态中(还可以清除主线程状态,避免主线程被冻结后程序停着) 守护线程:守护主线程,主线程结束后,守护线程将自动结束。 格式:t1.setDaemon(true); 在开启线程之前设定为守护线程 */ public class Test7 { public static void main(String[] args) { StopThread st=new StopThread(); Thread t1=new Thread(st); Thread t2=new Thread(st); t1.start(); t2.start(); int num=0; while(true){ if(num++==60){ t1.interrupt(); t2.interrupt(); //st.changeFlag();在改变之前就已经停止线程了 break; } System.out.println(Thread.currentThread().getName()+"...."+num);//主线程名为main } } } class StopThread implements Runnable{ private boolean flag=true; public synchronized void run(){ while(flag){ try{ wait(); }catch(InterruptedException e){ flag=false;//强制弄醒后改变标志,停止运行 System.out.println(Thread.currentThread().getName()+"...Exception"); } System.out.println(Thread.currentThread().getName()+"...run"); } } public void changeFlag(){ flag=false; } }
守护线程例子:
@org.junit.Test public void addition_isCorrect() { assertEquals(4, 2 + 2); Thread t1 = new Thread() { @Override public void run() { for (int i = 0; i < 500; i++) { System.out.println("..............i = " + i); } } }; Thread t2 = new Thread() { @Override public void run() { for (int i = 0; i < 2; i++) { System.out.println("..2"); } } }; t1.setDaemon(true);//true 代表将t1设置为守护线程 t1.start(); t2.start();//t2结束后 t1就自动结束(但是结束需要些时间) }
日志:
..2 ..............i = 0 ..............i = 1 ..............i = 2 ..............i = 3 ..2 ..............i = 4 ..............i = 5 ..............i = 6 ..............i = 7 ..............i = 8 ..............i = 9 ..............i = 10 ..............i = 11 ..............i = 12 ..............i = 13 ..............i = 14 ..............i = 15 ..............i = 16 ..............i = 17 ..............i = 18 ..............i = 19 ..............i = 20 ..............i = 21 ..............i = 22 ..............i = 23 ..............i = 24 ..............i = 25 ..............i = 26 ..............i = 27 ..............i = 28 ..............i = 29 ..............i = 30 ..............i = 31 ..............i = 32 ..............i = 33 ..............i = 34 ..............i = 35 ..............i = 36 ..............i = 37 ..............i = 38 ..............i = 39 ..............i = 40 ..............i = 41 ..............i = 42 ..............i = 43 ..............i = 44 ..............i = 45 ..............i = 46 ..............i = 47 ..............i = 48 ..............i = 49 ..............i = 50 ..............i = 51
/*join: 当A线程执行到了B线程的.join()方法时,A就回等待,等B线程执行完,A线程才会执行。 join可以用来临时加入线程执行 Thread.yield();//暂停当前线程 t1.setPriority(Thread.MAX_PRIORITY);将t1设置为最低优先级10 默认为5 最高为1 */ public class Test8 { public static void main(String[] args)throws Exception { Demo8 d=new Demo8(); Thread t1=new Thread(d); Thread t2=new Thread(d); t1.start(); t2.start(); t1.join();//代表t1抢到主线程的执行权,主线程没得执行权被处于冻结状态,等t1执行完所有程序,主线程才活过来 for(int x=0;x<80;x++){ System.out.println("main...."+x); } System.out.println("over"); } } class Demo8 implements Runnable{ public void run(){ for(int x=0;x<70;x++){ System.out.println(Thread.currentThread().getName()+"..."+x); } } }
锁与wait()的使用:
/** * 轮流打印 1 2 */ public static class OutputThread implements Runnable { private int num; private Object lock; public OutputThread(int num, Object lock) { super(); this.num = num; this.lock = lock; } public void run() { try { while(true){ synchronized(lock){ lock.notifyAll();//唤醒被该锁 锁住的线程 lock.wait();//当前线程等待 需要使用lock对象唤醒后才能继续执行 Thread.currentThread().sleep(1000); System.out.println(Thread.currentThread().getName() + "," + num); } } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args){ final Object lock = new Object(); Thread thread1 = new Thread(new OutputThread(1,lock),"线程1"); Thread thread2 = new Thread(new OutputThread(1, lock),"线程2"); thread1.start(); thread2.start(); } }