JAVA多线程 总结

来源:互联网 发布:vmware player linux 编辑:程序博客网 时间:2024/05/17 23:25

未完待续Loading...

通过继承自Thread:

class Player extends Thread{String name;Player(String name){this.name=name;}@Overridepublic void run(){for(int i=0;i<3;i++)System.out.println(name+"正在奔跑中"+i);}}public class ThreadTest {public static void main(String []args){Player aPlayer=new Player("刘翔");Player bPlayer=new Player("博尔特");aPlayer.start();bPlayer.start();}}

运行结果:
刘翔正在奔跑中0
博尔特正在奔跑中0
博尔特正在奔跑中1
博尔特正在奔跑中2
刘翔正在奔跑中1
刘翔正在奔跑中2

实现Runnable接口


class Car implements Runnable{String name;Car(String name){this.name=name;}@Overridepublic void run() {for(int i=0;i<3;i++)System.out.println(name+"正在运行中"+i);}}public class ThreadTest {public static void main(String []args){Car aCar=new Car("法拉利");Car bCar=new Car("奔驰");Thread t1=new Thread(aCar);Thread t2=new Thread(bCar);t1.start();t2.start();}}


运行结果:
奔驰正在运行中0
法拉利正在运行中0
法拉利正在运行中1
法拉利正在运行中2
奔驰正在运行中1
奔驰正在运行中2

继承自Thread与实现Runnable接口的区别

class Shop extends Thread{String name;Shop(String name){this.name=name;}private int count=3;@Overridepublic void run(){for(int i=0;i<3;i++)if(count>=0)System.out.println(name+"卖出一张票,还剩"+--count);}}public class ThreadTest {public static void main(String []args){Shop aShop=new Shop("窗口1 ");Shop bShop=new Shop("窗口2 ");aShop.start();bShop.start();}}

运行结果:
窗口1 卖出一张票,还剩2
窗口2 卖出一张票,还剩2
窗口1 卖出一张票,还剩1
窗口2 卖出一张票,还剩1
窗口1 卖出一张票,还剩0
窗口2 卖出一张票,还剩0

class Shop implements Runnable{private int count=5;@Overridepublic void run(){for(int i=0;i<20;i++)if(this.count>=0)System.out.println(Thread.currentThread().getName()+"卖出一张票,剩"+count--);}}public class ThreadTest {public static void main(String []args){Shop shop=new Shop();new Thread(shop,"窗口一").start();new Thread(shop,"窗口二").start();}}

运行结果:

窗口一卖出一张票,剩5
窗口二卖出一张票,剩4
窗口一卖出一张票,剩3
窗口二卖出一张票,剩2
窗口一卖出一张票,剩1
窗口二卖出一张票,剩0

main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,
完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使
用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实际在就是在操作系统中启
动了一个进程。

线程的强制执行:


public class ThreadTest implements Runnable {@Overridepublic void run(){for(int i=0;i<3;i++)System.out.println(Thread.currentThread().getName());}public static void main(String []args){ThreadTest threadTest=new ThreadTest();Thread thread=new Thread(threadTest,"线程");System.out.println("线程是否执行中"+thread.isAlive());thread.start();System.out.println("线程是否执行中"+thread.isAlive());for(int i=1;i<=5;i++){if(i>3){try{thread.join();}catch(Exception e){e.printStackTrace();}}System.out.println("主线程执行中"+i);}}}

运行结果:
线程是否执行中false
线程是否执行中true
主线程执行中1
主线程执行中2
主线程执行中3
线程
线程
线程
主线程执行中4
主线程执行中5

线程的休眠:


public class ThreadTest implements Runnable {@Overridepublic void run(){for(int i=0;i<3;i++){System.out.println("线程执行中"+i);try{Thread.sleep(1000);}catch(Exception e){e.printStackTrace();}}}public static void main(String []args){ThreadTest threadTest=new ThreadTest();new Thread(threadTest).start();}}

运行结果:
线程执行中0
线程执行中1
线程执行中2

线程中断:

当线程的 run 方法执行完,并由 return 语句返回时,或是在方法中出现了未捕获的异常,
线程都会终止,也可以通过 interrupt 方法请求中断线程。
当对线程调用 interrupt 方法时, 线程的中断状态将会置位。每个线程都具有boolean标志,
每个线程应当不时的检查这个标志,以判断线程是否中断。
如:
while(!Thread.currentThread().isInterrupted()&& more word to do){do mo work;}


但是如果线程阻塞,将无法检测中断状态,这里将会产生 InterruptedException 当一个阻塞
线程调用 interrupt 方法 将会被 InterruptedException 中断。
没有任何语言方面的需求要求一个被中断的线程应该终止,中断线程只是引起它的注意,被中断
线程可以决定如何相应中断,甚至处理完异常后继续执行,而不理会中断。更普遍的情况是,线程将
简单的将线程中断作为一个终止请求。该类 run 方法如:
public void run(){try{...while(!Thread.currentThread().isInterrupted()&& more work to do){do more work;}}catch(InterruptedException e){//Thread was interrupted during sleep or wait}finally{cleanup,if required;}//exiting the run method teminates the thread}

如果线程在中断状态被置位时调用 sleep 方法,并不会休眠,而是清除中断状态并且
抛出 InterruptedException 异常。
public class ThreadTest implements Runnable {@Overridepublic void run(){System.out.println("执行run方法");try {Thread.sleep(10000);System.out.print("线程完成休眠");} catch (InterruptedException e) {System.out.println("线程休眠被打断");return ;}finally{System.out.println("线程状态"+Thread.currentThread().isInterrupted());}System.out.println("线程正常终止");}public static void main(String []args){ThreadTest threadTest=new ThreadTest();Thread thread=new Thread(threadTest);thread.start();try {Thread.sleep(2000);} catch (Exception e) {e.printStackTrace();}thread.interrupt();}}

运行结果:
执行run方法
线程休眠被打断
线程状态false
void interrupt()/*向线程发送中断请求。线程的中断状态设置为true。如果当前线程被sleep调用阻塞,将会抛出 InterruptedException*/static boolean interrupted()//测试当前线程是否为被中断,会产生副作用将当前线程中断状态设置为 falseboolean isInterrupted() //测试线程是否被终止,不会改变中断状态static Thread currentThread() //返回当前执行线程的Thread对象


public class ThreadTest implements Runnable {@Overridepublic void run(){for(;;)System.out.println("线程执行中");}public static void main(String []args){ThreadTest threadTest=new ThreadTest();Thread thread=new Thread(threadTest);thread.setDaemon(true);thread.start();}}


虽然设置成为了死循环,但是程序还是可以运行完成的,因为死循环中的线程操作已经设
置在后台运行了。

线程同步:

public class ThreadTest implements Runnable {private int ticket=5;private int money=0;@Overridepublic void run(){String name=Thread.currentThread().getName();while(ticket>=1){sell();}}//sell 部分 进行售票收费操作public void sell(){String name=Thread.currentThread().getName();if(ticket>=1){System.out.println(name+"开始卖票了");ticket--;//执行一个无意义的耗时操作,让售票与收费可能不能一次性完成for(int j=1;j<=1000;j++)Math.sin(j);money++;System.out.println("ticket = "+ticket+"\t money= "+money);}}public static void main(String []args){ThreadTest threadTest=new ThreadTest();new Thread(threadTest,"窗口一").start();;new Thread(threadTest,"窗口二").start();}}

运行结果:
窗口一开始卖票了
窗口二开始卖票了
ticket = 3money= 2
ticket = 3money= 2
窗口二开始卖票了
窗口一开始卖票了
ticket = 1money= 3
ticket = 1money= 4
窗口二开始卖票了
窗口一开始卖票了
ticket = -1money= 5
ticket = -1money= 6


结果中出现了负数,这是因为线程不同步导致的后果,比如,当 ticket=1的时候,窗口一

线程判断条件 ticket>=1 进入 执行sell部分,首先打印出"窗口一开始售票",然后该部分没有
完全结束,这时ticket并没有执行 ticket-- 部分,所以仍然为 1,此时可能又轮到窗口二线程执
行,判断条件ticket>=1 于是又进入了 sell 部分,这时候就可以理解了,在ticket=1的时候,

两个线程都 已经 进入了sell的部分,自然会发生ticket=-1的情况了。

为了能够进行同步,可以通过以下几种方法修改上面的代码:

可以使用 使用 synchronized  关键字将 sell 方法设置为 同步方法:

synchronized public void sell(){//more code ...}

为类添加一个锁作为成员变量,并将 sell 方法修改,给出部分代码:

// more code ...private Lock lock=new ReentrantLock();public void sell(){lock.lock();try {if(ticket>=1){///more code ...}} finally{lock.unlock();}}//more code ...

设置同步之后,运行结果:

窗口一开始卖票了

ticket = 4money= 1

窗口一开始卖票了

ticket = 3money= 2

窗口二开始卖票了

ticket = 2money= 3

窗口二开始卖票了

ticket = 1money= 4

窗口一开始卖票了

ticket = 0money= 5


下面是个银行转账的例子:

程序主要功能是,已有100个账户,并且每个账户都预先有1000元的余额,然后为每个账户

开启了一个线程,用于将余额转账到一个随机的账户。

class Bank{private double accounts[];Bank(int n,double initMoney){accounts=new double[n];for(int i=0;i<n;i++)accounts[i]=initMoney;}   //从 from 账户 转 amount 元 到账户 to   public void transfer(int from,int to ,double amount){     if(accounts[from]<amount)return ;   System.out.print(Thread.currentThread().getName());   accounts[from]-=amount;   System.out.printf("%10.2f from %d to %d ", amount,from,to);   accounts[to]+=amount;   System.out.printf("Total money =%10.2f\n",getTotalMoney());}   //计算所有账户余额总和public double getTotalMoney(){double sum=0;for(double a:accounts)sum+=a;return sum;}//获取账户总数量public int getSize(){return accounts.length;}//获取指定账户余额public double getMoney(int from){return accounts[from];}}

public class ThreadTest implements Runnable{private Bank bank;private int fromAccount;//当前要转账的账户private double maxAmount;//转账最大额private int DELAY=10;public ThreadTest(Bank bank,int from,double max){this.bank=bank;fromAccount=from;maxAmount=max;}@Overridepublic void run(){try {while(true){int toAccount=(int)(bank.getSize()*Math.random());double amount=(long)(maxAmount*Math.random());bank.transfer(fromAccount, toAccount, amount);Thread.sleep((long)(DELAY*Math.random()));}} catch (InterruptedException e) {}}public static void main(String []args){Bank bank=new Bank(100, 1000);for(int i=0;i<100;i++)new Thread(new ThreadTest(bank, i, 1000)).start();}}

在没有进行同步的情况下,可以看到刚开始的时候,总额度没有什么问题,然而运行到了后期的

时候,突然发现账户总额出现了问题,原因上面已经说明,因为转出账户余额减少与转入账户余额增加
不是原子操作。

Thread-20     40.00 from 20 to 47 Total money = 100000.00

Thread-33    647.00 from 33 to 54 Total money = 100000.00

Thread-72    579.00 from 72 to 69 Total money = 100000.00

...

Thread-31    299.00 from 31 to 8 Total money =  99463.00

Thread-98    854.00 from 98 to 6 Total money =  99463.00


那可以通过增加锁的方式设置临界区,并修改 transfer方法:

//more code ...private Lock lock=new ReentrantLock();public void transfer(int from,int to ,double amount){  lock.lock();  try   {  if(accounts[from]<amount)return ;  //more code...  }   finally  {  lock.unlock();  }  }//more code ...

但是有时线程在进入临界区之后,发现还需要满足条件才能执行。就需要设置一个条件来管理

那些已经获得了锁却不能进行有效工作的进程.

但是如果只是单纯的进行这样的操作:

if(bank.getMoney(fromAccount)>=amount)transfer(fromAccount,toAccount,amount);

有可能在执行条件判断后,执行 transfer 方法前 线程就已经中断了 

而如果将 transfer 方法修改为这样,如果当获得了锁的进程中的余额不足时,将会一直等待其他

账户将余额转入,而因为已经获得了此锁,所以其它账户没有进行余额操作的机会.

public void transfer(int from,int to ,double amount){  lock.lock();  try   {  while(accounts[from]<amount);  //more code...  }   finally  {  lock.unlock();  }  }

于是可以使用条件对象,

//more code ...private Condition condition;public Bank(int n,double initMoney){condition=lock.newCondition();//more code ..}//more code ...public void transfer(int from,int to ,double amount){   lock.lock();   try    {  while(accounts[from]<=amount)  condition.await();  //more code ...   condition.signalAll();   }    catch (InterruptedException e) {e.printStackTrace();}    finally   {   lock.unlock();   }}//more code ...

如果当前账户余额不足,线程阻塞,并且会放弃锁,然后其它线程就有机会继续获得此锁。等待获得锁

的线程与调用 awit 方法的线程有本质的区别。一旦一个线程调用 awit 方法,将会进入当前条件的等待集
当锁可用时,该线程不能马上解除阻塞,它会继续处于阻塞状态,直到其他线程调用 signalAll 方法。这一
方法仅仅只是通知因为该条件而进入等待集的所有进程,将这些进程从等待集中移除,他们将被激活,一旦锁
继续可用,将回试图重新进入对象,从 awit 犯法调用处返回,获得该锁并从被阻塞的地方继续执行.

Condition newCondition()//返回一个与该锁相关的条件对象void awit()//将该线程放到条件的等待集中void signalAll()//解除该条件等待集中所有线程的阻塞状态void signal()//解除该条件等待集中某一个线程的阻塞状态


0 0
原创粉丝点击