基本的概念:
多线程其实就是进程中一个独立的控制单元或者说是执行路径,线程控制着进程的执行,【重点】一个进程中,至少有一个线程存在。
目录:
【1】【线程的创建】怎样继承Thread类,创建多个线程?这个和实现Runnable接口创建的方式有什么区别?
【2】创建线程为什么要覆写run方法和为什么在main方法中执行线程程序,每一次运行的效果都不一样?
【3】怎样获得当前线程的名称?
【4】【重点】线程中存在的安全问题是什么,怎样发现线程安全?
【5】【重点】实例分析:汽车站售票程序【其中涉及了线程同步】
【6】同步的两种表现形式是什么?
【7】懒汉式单例模式分析
【8】多线程的死锁问题
【9】JDK1.5中关于线程的新特性
【10】多线程之间的通信模式分析
【11】停止线程的方法
【12】join和yield方法总结
【13】开发过程中为了提高效率,怎样单独封装?
我根据实例来总结:分享给大家
【1】继承Thread类创建线程和实现Runnable接口来创建线程:
Thread类是一个线程类,我们在具体定义定义多线程开发时,有两种方式创建线程,其中一种就是通过继承的方式来完成,覆写Thread类中的run方法:但是这种创建方式有一定的弊端:那就是被创建的子类不能再继承其他的类;为了解决这种弊端,一般我们都直接去实现Runnable接口去实现多线程的创建,这其实也正是JAVA中解决多态性的具体方案,实现一个接口之后,也能去继承其他的父类或者被子类继承!
原理明白了,好了,写个简单的程序演示演示吧:
[java] view plaincopy
- class Mythread extends Thread//这里就是一个简单的继承创建线程的方式
- {
- public void run()//覆写了父类中的run方法,定义了自定义的函数主体for循环
- {
- for (int i=0;i<50 ;i++ )
- {
- System.out.println("thread hao!="+i);//仅仅是演示线程执行的内容
- }
- }
- }
- //这里就是通过main方法来调用
- class ThreadDemo1
- {
- public static void main(String[] args)
- {
- Mythread mt=new Mythread();//把这个线程对象实例化
- mt.start();//调用父类的start方法,启动并执行线程
- }
- }
[java] view plaincopy - class Cus implements Runnable//这里是通过实现Runnable接口的方式创建多线程的
- {
- public void run()//同理也要覆写接口的run方法
- {
- for (int i=0;i<3;i++ )//简单的函数主体
- {
- System.out.println("....."+i);//用于演示而已
- }
- }
- }
- class SynchronizedDemo4
- {
- public static void main(String[] args)
- {
- Cus c=new Cus();//创建接口的子类对象
- Thread c1=new Thread(c);//定义了两个线程,把接口的子类对象和线程相关联起来
- Thread c2=new Thread(c);
- c1.start();//启动两个线程并且执行
- c2.start();
- }
- }
【2】为什么要复写run方法:
复写run方法主要是因为:在Thread类中,run方法是储存线程要运行的重要代码,所以在定义的时候,我们必须根据具体的需求去覆写run方法,从而自定义要实现的功能主体!
start方法是父类开启和执行线程的方法,直接调用就行了,但是我们如果不覆写run方法,直接在子类中调用的话,是毫无意义的,因为这样的话,相当于我们只是创建了线程,而没有运行!
在main方法中,多次运行多线程的结果都不一定一样的原因是:我们知道,多线程在执行的时候,都是在获取cpu的一个执行权,cpu说想把执行权给谁(线程),谁(线程)就去执行相应的操作!但是在某一时刻,只能有一个程序在运行,当然多核除外
这也就反应除了多线程的一个重要特性:那就是随机性!(可以理解为:谁抢到谁就执行)
【3】获取当前线程的名称:
很简单了:用线程类的静态方法就可以:
Thread.currentThread().getName():这样就能够获得当前线程的名称:
线程名称的基本格式是:Thread-编号,编号都是从0开始的
【4】发现线程安全:
可以模拟出线程在运行中出现的问题:在这里卖票系统中就是最好的分析
一般通过下列代码就能找到其安不安全的地方!因为现在只要在这里休眠一下,其他线程自然会获得cpu的执行权进来,这样就没法保证在共享的代码中,不出现问题:在不同步的情况下,售票窗口可能会售出0号,甚至是-1号票,这样就要求我们必须要保证共享数据的同步性:
对于发现线程的安全问题:
具体步骤:
分析:哪里出现了线程的安全问题
如何去找原因:
1.明确哪些代码是多线程运行的代码:
2.明确哪里是共享的数据
3.明确多线程的运行代码中哪里涉及到了要执行共享的数据
[java] view plaincopy
- <span style="white-space:pre"> </span>//这种简单的方式只是模拟出现问题而已:
[java] view plaincopy - <span style="white-space:pre"> </span>try
- {
- Thread.sleep(10);
- }
- catch (Exception e)
- {
- }
【5】汽车站售票小程序:
模拟卖票窗口,4个窗口同时卖100张共同的票
[java] view plaincopy - /*
- 实现Runnable接口的具体步骤和解决安全性的问题
- */
- //1、继承Thread类的做法:
- /*
- class Ticket extends Thread
- {
- private static int tickets=100;
- //为了确保票的唯一性,必须用static修饰,否则每个线程窗口会各自卖100张票
- //覆写父类的run方法,定义线程卖票方式
- public void run()
- {
- while (true)
- {
- if (tickets>0) //当票大于0时,才能出票
- {
- //打印结果,获取各线程(窗口)卖票情况
- System.out.println(Thread.currentThread().getName()+".....sale:"+tickets --);
- }
- }
- }
- }
- class ThreadDemo3
- {
- public static void main(String[] args)
- {
- Ticket t1=new Ticket();
- Ticket t2=new Ticket();
- Ticket t3=new Ticket();
- Ticket t4=new Ticket(); //创建4个窗口(4个线程)
- t1.start();
- t2.start();
- t3.start();
- t4.start(); //启动并执行窗口的卖票
- /*
- 部分结果:
- Thread-2.....sale:76
- Thread-2.....sale:75
- Thread-2.....sale:74
- Thread-3.....sale:80
- Thread-1.....sale:81
- Thread-1.....sale:71
- Thread-1.....sale:70
- Thread-3.....sale:72
- Thread-2.....sale:73
- Thread-0.....sale:78
- Thread-2.....sale:67
- Thread-2.....sale:65
- Thread-3.....sale:68
- Thread-1.....sale:69
- Thread-3.....sale:63
- Thread-2.....sale:64
- }
- }
- */
- //实现Runnable接口
- class Ticket implements Runnable
- {
- private int tickets=100;
- Object obj=new Object();
- public void run()
- {
- while (true)
- {
- synchronized(obj)
- {
- if (tickets>0)
- {
- /*这里是模拟cpu让进来的某一个线程没有分配到执行权,必然会售出不正常的票
- try
- {
- Thread.sleep(10);
- }
- catch (Exception e)
- {
- }
- */
- //为了解决这样的安全问题,要用到同步代码块
- try
- {
- Thread.sleep(10);
- }
- catch (InterruptedException e)
- {
- }
- System.out.println(Thread.currentThread().getName()+"....sale:"+tickets --);
- }else
- {
- break;
- }
- }
- }
- }
- }
- class ThreadDemo3
- {
- public static void main(String[] args)
- {
- Ticket t=new Ticket();
- Thread t1=new Thread(t);
- Thread t2=new Thread(t);
- Thread t3=new Thread(t);
- Thread t4=new Thread(t);
- /*自定义线程名称的方式:
- Thread t1=new Thread(t,"窗口1");
- Thread t2=new Thread(t,"窗口2");
- Thread t3=new Thread(t,"窗口3");
- Thread t4=new Thread(t,"窗口4");
- 部分结果:
- 窗口1....sale:44
- 窗口1....sale:43
- 窗口1....sale:42
- 窗口1....sale:41
- 窗口1....sale:40
- 窗口4....sale:80
- 窗口2....sale:81
- 窗口2....sale:37
- 窗口2....sale:36
- 窗口2....sale:35
- 窗口2....sale:34
- 窗口2....sale:33
- 窗口2....sale:32
- 窗口2....sale:31
- */
- t1.start();
- t2.start();
- t3.start();
- t4.start();
- }
- }
【6】同步的两种表现形式:
一种是代码块同步,一种是函数同步:
[html] view plaincopy
- /*
- 练习:
- 银行有一个小金库,它有两个顾客,每个顾客向金库中各存300元钱
- ,分三次存入,一次是存入100元
- 【注意】
- 分析:哪里出现了线程的安全问题
- 如何去找原因:
- 1.明确哪些代码是多线程运行的代码:
- 2.明确哪里是共享的数据
- 3.明确多线程的运行代码中哪里涉及到了要执行共享的数据
- */
- /*
- //定义银行类
- class Bank
- {
- //定义金库
- private int sum=0;
- Object obj=new Object(); //定义了一个万能的上帝对象,去实现同步代码块
- //定义银行中的存钱方式
- public void add(int n)
- {
- synchronized(obj)
- {
- sum += n; //相当于sum=sum+n;(1)
- try
- {
- Thread.sleep(10);
- }
- catch (InterruptedException i)
- {
- }
- System.out.println(Thread.currentThread().getName()+"sum="+sum);//(2)
- //综上所述的分析方法:得知(1)和(2)是多线程中都要执行的共享数据
- //所以对此代码块必须实现同步,上面的sleep方法是模拟了出问题的位置
- }
- }
- }
- */
- /*
- 看上述的代码块知道:其实现同步的代码其实就是银行中的add方法代码主体
- 所以既然定义了同步该方法主体的同步,同理也可以定义add方法函数的同步、
- 所以便有了下列的定义函数的同步的方法,很简单易行
- */
- class Bank
- {
- private int sum=0;
- public synchronized void add(int n)
- {
- sum += n;
- try
- {
- Thread.sleep(10);
- }
- catch (InterruptedException i)
- {
- }
- System.out.println(Thread.currentThread().getName()+": sum="+sum);
- }
- }
- class Cus implements Runnable
- {
- private Bank b=new Bank();
- public void run()
- {
- for (int i=0;i<3;i++ )
- {
- b.add(100);
- }
- //为什么不在for循环上加同步,主要考虑到加了同步,意思就是要等到进来的某一线程(即顾客)
- //依次存完之后,才能让其他的顾客存钱,那样的话就是有序的了
- }
- }
- class SynchronizedDemo4
- {
- public static void main(String[] args)
- {
- Cus c=new Cus();
- Thread c1=new Thread(c);
- Thread c2=new Thread(c);
- c1.start();
- c2.start();
- /*结果
- Thread-0: sum=100
- Thread-0: sum=200
- Thread-0: sum=300
- Thread-1: sum=400
- Thread-1: sum=500
- Thread-1: sum=600
- */
- }
- }
【7】懒汉式单例模式具体分析:
[java] view plaincopy
- /*
- 写两种方式的单例模式:注意其区别
- */
- /*
- 饿汉式的单例模式
- class Single
- {
- private static final Single s=new Single();
- private Single(){}
- public static Single getInstance()
- {
- return s;
- }
- }
- */
- /*
- 懒汉式的单例模式(延迟加载)
- */
- class Single
- {
- private static Single s=null;
- private Single(){}
- //1.为了解决这种安全问题,可以同步函数的方法解决,但是每个线程进来都必须判断锁,效率比较低
- //private static synchronized Single getInstance()
- //2.为了提高效率,可以用同步代码块的方法解决
- public static Single getInstance()
- {
- if (s==null) //只要有一个线程执行成功,那么以后进来的线程就不再判断锁了,
- //因为s已经不为null了,直接不能创建单例的对象了,直接用就行了
- {
- synchronized(Single.class)
- {
- if (s==null)
- {
- //--0线程
- //--1线程
- //当0线程醒了,会创建一个对象
- //当1线程醒了,也会创建一个对象
- //……这就是出现了安全问题,不是单例模式了
- s=new Single();
- System.out.println(Thread.currentThread().getName()+"访问成功!");
- }
- }
- }
- return s;
- }
- }
- class SingleDemo6
- {
- public static void main(String[] args)
- {
- Single.getInstance();//在main方法中通过单例类对外提供的getInstance方法访问
- //结果:main访问成功!
- }
- }
【8】多线程的死锁问题:
什么是死锁:两个线程彼此之间抢锁的情况,互不相让,就产生死锁
代码分析:
[java] view plaincopy
- /*
- 自己写一个死锁程序
- 用锁的嵌套方式来模拟死锁
- 模拟了两个线程彼此之间抢锁的情况,互不相让,就产生死锁
- */
- class TestDeaDLock implements Runnable
- {
- private boolean flag;//设置flag是为了让线程进入不一样的锁中去执行
- TestDeaDLock(boolean flag)//在Runnable子类初始化时就设置flag
- {
- this.flag=flag;
- }
- public void run()
- {
- if (flag)
- {
- synchronized(MyLock.m1)
- {
- System.out.println("true:----lock! m1 ");
- //演示:拿着锁1的线程想进入具备锁2的内容中去,必然要拿到锁2才行
- //此时持有锁2的线程可能会把锁2给持着锁1的线程,但是也可能互不想让,导致死锁的产生
- synchronized(MyLock.m2)
- {
- System.out.println("true:----lock m2 !");
- }
- }
- }else
- {
- synchronized(MyLock.m2)
- {
- System.out.println("false:----lock m2 !");
- //演示:拿着锁2的线程想进入具备锁1的内容中去,必然要拿到锁1才行
- synchronized(MyLock.m1)
- {
- System.out.println("false:----lock m1 !");
- }
- }
- }
- }
- }
- class MyLock //定义自己的锁类,其中有两种锁
- {
- static MyLock m1=new MyLock(); //设置锁1
- static MyLock m2=new MyLock();//设置锁2
- }
- class DeadLockDemo7
- {
- public static void main(String[] args)
- {
- Thread t1=new Thread(new TestDeaDLock(true)); //一个设置为true
- Thread t2=new Thread(new TestDeaDLock(false));//一个设置为false,主要是为了能够进入不一样的锁中执行
- t1.start();
- t2.start();
- /*
- 演示结果可能会出现:
- true:----lock! m1
- false:----lock m2 !
- */
- }
- }
【9】JDK1.5中线程的新特性:
我们都知道,在线程中有一个很重要的机制,那就是等待唤醒机制:例如代码分析:
[java] view plaincopy - /*
- 线程的等待唤醒机制
- 代码的优化
- 【优化的重点】
- 1.资源中:包含基本的属性,存入,取出函数
- 2.存入类:直接调用资源的存入函数
- 3.取出类:直接调用资源的取出函数
- 这样看起来非常符合逻辑,层次清楚,便于代码的维护和修改等
- */
- //定义了一个共同的资源
- class Res
- {
- private String name;
- private String sex;
- boolean flag=false;
- public synchronized void set(String name,String sex)
- {
- if (flag)
- try{wait();}catch(Exception e){}
- this.name=name;
- this.sex=sex;
- this.flag=true;
- this.notify();
- }
- public synchronized void out()
- {
- if (!flag)
- try{wait();}catch(Exception e){}
- System.out.println("name="+name+" ....... sex="+sex);
- this.flag=false;
- this.notify();
- }
- }
- //定义存入资源类
- class Input implements Runnable
- {
- Res r;
- //存入类在初始化时,就有资源
- Input(Res r)
- {
- this.r=r;
- }
- //覆写run方法
- public void run()
- {
- int id=0;
- while (true)
- {
- //加锁为了保证线程的同步,只能允许一个线程在其中操作直到完成位置
- if (id==0)
- r.set("crystal","man");
- else
- r.set("杨女士","女");
- id=(id+1)%2;
- }
- }
- }
- //定义取出资源类
- class Output implements Runnable
- {
- Res r;
- Output(Res r)
- {
- this.r=r;
- }
- public void run()
- {
- while (true)
- {
- r.out();
- }
- }
- }
- /*
- 上述的存入资源中和取出资源中:共享的代码就是存入操作和取出操作,一定要保证通过进行,
- 否则,当一个线程进入时,存完一个之后,再进入存第二时(存完名称时,挂了),这是取出资源的线程
- 在运行,这时候就会把出现:r.name="杨女士"和r.sex="man";的情况
- */
- class InputOutputDemo3
- {
- public static void main(String[] args)
- {
- /*
- Res r=new Res(); //资源实例化
- Input in=new Input(r); //把资源和Runnable子类相关联
- Output out=new Output(r);
- Thread t1=new Thread(in);//定义了两个不同线程,一个存入,一个取出
- Thread t2=new Thread(out);
- t1.start(); //开启并执行线程
- t2.start();
- */
- Res r=new Res();
- new Thread(new Input(r)).start();
- new Thread(new Output(r)).start();
- /*result:
- …………
- …………
- name=crystal ....... sex=man
- name=杨女士 ....... sex=女
- name=crystal ....... sex=man
- name=杨女士 ....... sex=女
- name=crystal ....... sex=man
- name=杨女士 ....... sex=女
- name=crystal ....... sex=man
- name=杨女士 ....... sex=女
- name=crystal ....... sex=man
- name=杨女士 ....... sex=女
- name=crystal ....... sex=man
- name=杨女士 ....... sex=女
- name=crystal ....... sex=man
- name=杨女士 ....... sex=女
- name=crystal ....... sex=man
- name=杨女士 ....... sex=女
- name=crystal ....... sex=man
- name=杨女士 ....... sex=女
- name=crystal ....... sex=man
- …………
- …………
- */
- }
- }
那么在这里面用到了同步锁,在JDK1.5中,新特性出现了:
将同步synchronized替换成为了Lock操作
将Object中的wait,notify,notifyAll,替换成了Condition对象
该对象可以Lock锁,进行获取
在该例子中,实现了本方只唤醒对方的操作
用这种新特性:程序会进行得非常完美,完全符合逻辑!不浪费资源
[java] view plaincopy - import java.util.concurrent.locks.*;
- class Resource
- {
- private String name;
- private int id=1;
- boolean flag=false;
- private Lock lock=new ReentrantLock();
- private Condition condition_pro=lock.newCondition();
- private Condition condition_con=lock.newCondition();
- //定义生产者的同步生产函数
- public void set(String name)
- {
- lock.lock(); //进来就获得锁进入
- try
- {
- while (this.flag)
- //condition.await(); //首次进来因为为false,所以直接执行下面的语句
- condition_pro.await();//让自身的线程等待
- this.name=name+"....."+id++;
- System.out.println(Thread.currentThread().getName()+" Producer:----"+this.name);
- this.flag=true;
- //condition.signalAll();//唤醒线程池中所有的线程,为的是让不要出现全部等待的状况
- condition_con.signal();//不用唤醒所有了,只需要唤醒线程池中的对方就行,所以不用All了
- }
- catch (Exception e)
- {
- }
- finally
- {
- lock.unlock(); //最终释放锁
- }
- }
- //定义消费者的同步消费函数
- public void out()
- {
- lock.lock();
- try
- {
- while(!(this.flag))
- //condition.await();
- //同理:
- condition_con.await(); //本方等待
- System.out.println(Thread.currentThread().getName()+" Consumer:"+name);
- this.flag=false;
- //condition.signalAll();
- condition_pro.signal(); //唤醒对方
- }
- catch (Exception e)
- {
- }
- finally
- {
- lock.unlock();
- }
- }
- }
- //定义生产者具备调用生产功能
- class Producer implements Runnable
- {
- private Resource res;
- Producer(Resource res)
- {
- this.res=res;
- }
- public void run()
- {
- while (true)
- {
- res.set("汽车");
- }
- }
- }
- //定义消费者具备消费功能
- class Consumer implements Runnable
- {
- private Resource res;
- Consumer(Resource res)
- {
- this.res=res;
- }
- public void run()
- {
- while (true)
- {
- res.out();
- }
- }
- }
- class ProducerConsumerDemo6
- {
- public static void main(String[] args)
- {
- //把商品资源实例化
- Resource res=new Resource();
- //定义了两个生产者路线(线程)和两个消费者路线(线程)
- //并且同时启动这些线程和执行
- new Thread(new Producer(res)).start();
- new Thread(new Producer(res)).start();
- new Thread(new Consumer(res)).start();
- new Thread(new Consumer(res)).start();
- /*
- 结果:
- ………………
- Thread-1 Producer:----汽车.....3522
- Thread-2 Consumer:汽车.....3522
- Thread-0 Producer:----汽车.....3523
- Thread-3 Consumer:汽车.....3523
- Thread-1 Producer:----汽车.....3524
- Thread-2 Consumer:汽车.....3524
- Thread-0 Producer:----汽车.....3525
- Thread-3 Consumer:汽车.....3525
- Thread-1 Producer:----汽车.....3526
- Thread-2 Consumer:汽车.....3526
- Thread-0 Producer:----汽车.....3527
- Thread-3 Consumer:汽车.....3527
- Thread-1 Producer:----汽车.....3528
- Thread-2 Consumer:汽车.....3528
- Thread-0 Producer:----汽车.....3529
- Thread-3 Consumer:汽车.....3529
- Thread-1 Producer:----汽车.....3530
- ………………
- */
- }
- }
【10】多线程之间的通信模式分析:
多线程之间的通信就是:多个线程对象去访问共享的数据空间,但是每个线程具体的功能是不一样的,例如:有一个资源,一个线程是向其中存,另一个线程是往里面取出……
[java] view plaincopy
- /*
- 线程之间的通信:
- */
- //定义了一个共同的资源
- class Res
- {
- String name;
- String sex;
- }
- //定义存入资源类
- class Input implements Runnable
- {
- Res r;
- //存入类在初始化时,就有资源
- Input(Res r)
- {
- this.r=r;
- }
- //覆写run方法
- public void run()
- {
- int id=0;
- while (true)
- {
- //加锁为了保证线程的同步,只能允许一个线程在其中操作直到完成位置
- synchronized(r)
- {
- if (id==0)
- {
- r.name="crystal";
- r.sex="man";
- }else
- {
- r.name="杨女士";
- r.sex="女";
- }
- id=(id+1)%2;
- }
- }
- }
- }
- //定义取出资源类
- class Output implements Runnable
- {
- Res r;
- Output(Res r)
- {
- this.r=r;
- }
- public void run()
- {
- while (true)
- {
- //加锁为了保证线程的同步
- //取出资源也是一样的,必须保证同步,只能有一个线程操作
- synchronized(r)
- {
- System.out.println(r.name+"....."+r.sex);
- }
- }
- }
- }
- /*
- 上述的存入资源中和取出资源中:共享的代码就是存入操作和取出操作,一定要保证通过进行,
- 否则,当一个线程进入时,存完一个之后,再进入存第二时(存完名称时,挂了),这是取出资源的线程
- 在运行,这时候就会把出现:r.name="杨女士"和r.sex="man";的情况
- */
- class InputOutputDemo1
- {
- public static void main(String[] args)
- {
- Res r=new Res(); //资源实例化
- Input in=new Input(r); //把资源和Runnable子类相关联
- Output out=new Output(r);
- Thread t1=new Thread(in);//定义了两个不同线程,一个存入,一个取出
- Thread t2=new Thread(out);
- t1.start(); //开启并执行线程
- t2.start();
- /*result:
- ………………
- ………………
- 杨女士.....女
- 杨女士.....女
- 杨女士.....女
- crystal.....man
- crystal.....man
- ………………
- ………………
- */
- }
- }
【11】停止线程的方法总结:
停止线程的方法:
由于stop方法已经过时了
那么怎样才能停止线程呢?
思想只有一种,那就是让run方法结束
一般开启多线程的运行,运行代码通常是循环结构
所以只要控制好循环,就能让run方法结束,也就是让线程结束
让线程停止的3中方法:
1.设置标记:主线程结束之前改变标记让run方法结束
2.使用interrupt():当线程处于wait(必须在同步中进行)或者sleep中断时(在冻结的区域中),用此方法清除冻结,使线程回到运行中,这时会抛出异常,就在异常中设置标记,结束run方法,让线程不再进入去继续等待
3.使用守护线程的方法,setDaemon(true):在【线程启动之前】就设定为守护线程,主要是为了当主线程结束时,后台会自动结束被守护的线程
【其实我们看到的都是前台的线程,后台也有线程在运行,可以理解为后台依赖前台的关系,当前台结束了,后台线程也就over了,这就是守护线程的特点】
[java] view plaincopy
- class StopThread implements Runnable
- { private boolean flag=true;
- //在同步中,当线程进入冻结状况时,就不会读到标记了,线程就不会结束
- public synchronized void run()
- {
- while (flag)
- {
- try
- {
- wait(); //两个线程依次进来都是在这里等待了……没法被唤醒
- //所以该线程没有被停止掉,并且主线程中改变标记的方法,在这里,线程也无法读到了
- //在这个时候就要用到线程中的interrupt方法,主要是为了让线程从冻结状态回到运行状态上
- //但是在运行之后,依然会进入等待状态!
- //当回到运行状态的时候,就需要在抛出的异常中去处理标记,改变标记值就OK了,当看到标记为false的
- //时候两个线程就无法进入该方法区了,这样线程就结束了
- }
- catch (InterruptedException e)
- {
- System.out.println(Thread.currentThread().getName()+".....Exception");
- flag=false;
- }
- System.out.println(Thread.currentThread().getName()+" run .....");
- }
- }
- public void setFlag()
- {
- flag=false;
- }
- }
- class StopDemo7
- {
- public static void main(String[] args)
- {
- StopThread st=new StopThread();
- Thread t1=new Thread(st);
- Thread t2=new Thread(st);
- //t1.setDaemon(true);//分别把t1,t2线程都设置为守护线程后再启动
- //t2.setDaemon(true);
- t1.start();
- t2.start();
- int i=0;
- while (true)
- {
- if (i++ == 50)
- {
- st.setFlag(); //主线程中改变了标记,让其run方法主体结束,线程必然也结束了
- //t1.interrupt();
- //t2.interrupt();
- //在主线程结束之前把处于等待的线程用interrupt()方法清除掉冻结的线程,让其回到运行状态
- break;
- }
- System.out.println(Thread.currentThread().getName()+" ....."+i);
- }
- /*结果,这里是通过改变标记的方法,结束run方法体,让线程无法进入,直到主线程结束
- ,其他两个线程也结束了
- …………
- main .....41
- main .....42
- Thread-0 run .....
- main .....43
- main .....44
- main .....45
- main .....46
- main .....47
- main .....48
- main .....49
- main .....50
- Thread-1 run .....
- Thread-0 run .....
- */
- /*
- 运用了中断线程,清除了冻结的方法之后:两个线程也结束了,这是在同步中的做法
- main .....38
- main .....39
- main .....40
- main .....41
- main .....42
- main .....43
- main .....44
- main .....45
- main .....46
- main .....47
- main .....48
- main .....49
- main .....50
- Thread-0.....Exception
- Thread-0 run .....
- Thread-1.....Exception
- Thread-1 run .....
- */
- /*
- 设置为守护线程后运行的结果:不论是不是同步,主线程结束,后台线程就被Over了
- ………………
- main .....35
- main .....36
- main .....37
- main .....38
- main .....39
- main .....40
- main .....41
- main .....42
- main .....43
- main .....44
- main .....45
- main .....46
- main .....47
- main .....48
- main .....49
- main .....50
- */
- }
- }
【12】join()和yield()方法的总结:
《1》首先join方法:
join():方法的作用
1.当线程A执行到了线程B的B.join()方法时,A就会释放执行权给B,自己处于等待的冻结状态;
2.当线程B都执行完之后,线程A才能从冻结状态回到运行状态去执行;
3.所以join可以用来临时加入线程执行内容!
了解:
线程A把执行权释放了,让线程B去执行,自己冻结,这时线程B如果被等待了,线程A也没法回到运行状态
那么,这时就要用到interrupt()方法,去中断清除A的冻结,从而回到运行状态,当然也可以中断线程A
只是会受到伤害(抛出异常),在异常中处理就行了实际开发中:一般都是使用匿名内部类来完成的
[java] view plaincopy
- class TestJoin implements Runnable
- {
- public void run()
- {
- for (int i=0;i<80 ;i++ )
- {
- System.out.println(Thread.currentThread().getName()+".....run....."+i);
- }
- }
- }
- class JoinDemo8
- {
- public static void main(String[] args)
- {
- TestJoin tj=new TestJoin();
- Thread t1=new Thread(tj);
- Thread t2=new Thread(tj);
- t1.start();
- //try{t1.join();}catch(Exception e){}
- //执行的结果是:线程0全部把run函数执行完之后,主线程mian和线程1才交替执行
- //所以:join的功能是能够临时获得主线程的执行权,此程序中,主线程main把执行权释放给了t1,
- //自己处于了冻结状态,当t1线程结束之后,主线程才回到运行状态和t2线程继续交替执行
- t2.start();
- try{t1.join();}catch(Exception e){}
- //如果t1.join()处于这里的话:主线程仍然是把自己的执行权释放给了t1,自己处于冻结状态,
- //但是t2也是存活的线程,这时候cpu会自动发放执行权给t1或者t2去交替执行!主线程就悲催了,只有等到
- //t1执行完之后,才能拿到自己的执行权,从冻结状态回到运行状态
- for (int i=0; i<90; i++)
- {
- System.out.println(Thread.currentThread().getName()+".....mian....."+i);
- }
- }
- }
《2》yield()方法:
暂停当前正在执行的线程对象,并执行其他线程。
当线程A进入时,暂停当前正在执行的内容,释放执行权,让其他的线程进来执行,当其他的进来执行
之后,又重新获得执行权,继续执行,这样一来,等同于被共享的线程执行内容是“交替执行的‘
[java] view plaincopy - class TestSetPriority implements Runnable
- {
- int i=100;
- public void run()
- {
- for (int i=0; i<20 ; i++)
- {
- System.out.println(Thread.currentThread().toString()+"....run...."+i);
- Thread.yield();
- /*
- Thread[Thread-0,5,main]....run....0
- Thread[Thread-1,5,main]....run....0
- Thread[Thread-0,5,main]....run....1
- Thread[Thread-1,5,main]....run....1
- Thread[Thread-0,5,main]....run....2
- Thread[Thread-1,5,main]....run....2
- Thread[Thread-0,5,main]....run....3
- Thread[Thread-1,5,main]....run....3
- Thread[Thread-0,5,main]....run....4
- Thread[Thread-1,5,main]....run....4
- Thread[Thread-0,5,main]....run....5
- Thread[Thread-1,5,main]....run....5
- */
- }
- }
- }
- class SetPriorityDemo9
- {
- public static void main(String[] args)
- {
- TestSetPriority sd=new TestSetPriority();
- Thread t1=new Thread(sd);
- Thread t2=new Thread(sd);
- t1.start();
- t2.start();
- /*
- for (int i=0; i<20; i++)
- {
- System.out.println(Thread.currentThread().toString()+"....mians....");
- /*
- Thread[main,5,main]....mian....
- Thread[main,5,main]....mian....
- Thread[main,5,main]....mian....
- }
- */
- /*结果://这里是toString()方法,该线程类覆写了Object的这个方法
- 在其中封装了进了线程名称,线程优先级,线程组
- …………………………
- Thread[Thread-0,5,main]....run....
- Thread[Thread-1,5,main]....run....
- Thread[Thread-0,5,main]....run....
- Thread[Thread-1,5,main]....run....
- Thread[Thread-0,5,main]....run....
- Thread[Thread-1,5,main]....run....
- Thread[Thread-0,5,main]....run....
- Thread[Thread-1,5,main]....run....
- Thread[Thread-0,5,main]....run....
- Thread[Thread-1,5,main]....run....
- Thread[Thread-0,5,main]....run....
- Thread[Thread-1,5,main]....run....
- ………………………………
- */
- }
- }
【13】开发过程中为了提高效率,怎样单独封装?
实际开发中:一般都是使用匿名内部类来完成的 用法:在独立运算中:相互不相干扰的时候,可以单独封装一下,提高了执行效率 下面就是三个线程同时执行,很高效!
[java] view plaincopy - class StandardThreadDemo10
- {
- public static void main(String[] args)
- {
- //相当于继承的方法
- new Thread(){
- public void run()
- {
- for (int i=0; i<5 ;i++ )
- {
- System.out.println(Thread.currentThread().getName()+"...."+i);
- }
- }
- }.start();
- for (int i=0;i<6;i++ )
- {
- System.out.println(Thread.currentThread().getName()+"...."+i);
- }
- //相当于实现接口的方法
- Runnable r=new Runnable()
- {
- public void run()
- {
- for (int i=0; i<10 ;i++ )
- {
- System.out.println(Thread.currentThread().getName()+"...."+i);
- }
- }
- };
- new Thread(r).start();
- /*
- main....0
- Thread-0....0
- main....1
- Thread-0....1
- main....2
- Thread-0....2
- main....3
- Thread-0....3
- main....4
- Thread-0....4
- main....5
- Thread-1....0
- Thread-1....1
- Thread-1....2
- Thread-1....3
- Thread-1....4
- Thread-1....5
- Thread-1....6
- Thread-1....7
- Thread-1....8
- Thread-1....9
- */
- }
- }
学习借鉴自:http://blog.csdn.net/wkz_crystal/article/details/44919871
转载于:https://blog.51cto.com/13545923/2053560