Java上路09

记得九几年小学课本里有一篇华罗庚的文章写的是统筹学,里面的例子大概是这么说的:我们想喝茶,第一种办法,洗刷茶壶,放上茶叶,烧水,等水开了泡茶;第二种办法,烧水,等待水开的时间里洗刷茶壶,放上茶叶,然后水开了泡茶。

生活中的统筹学也就是编程思想中的算法。在此之前我们写的所有程序都可以说是单线程的。因为都是按部就班的干了一样再干另一样。而多线程达到了事半功倍的效果,一个时刻可以进行多个工作。

多线程的另一个理解是多个员工在同时为了一个任务进行工作,而不是单线程中自始至终只有一个人在劳动。

一. 多线程的实现,继承Thread:

Java中的Thread类是线程类,用它可以创建新的线程对象。

//继承自线程类class MyThread extends Thread{       //重写       public void run()       {              //自定义的新的线程执行的命令              for (int i=0; i<100; i++)              {                     System.out.println("新线程:"+i);              }       }} class ThreadTest{       public static void main(String[] args)       {              MyThread mt=new MyThread();       //创建新的线程对象              mt.start();     //开启新线程,并调用run方法               //主线程执行的命令              for (int j=0; j<100; j++)              {                     System.out.println("多线程测试:"+j);              }       }}      

图中展示两个进程并不是一一交替的,这是因为线程在CPU中优先权的问题。线程自己争取CPU资源以优先处理。我的CPU是单核单线程的,系统是32位XP,因此虽然程序是多线程的,但我的电脑并不能实现多线程,而是模拟。当然这个我们可以不考虑,只要知道多线程可以提高效率即可。

再来看看我们喝茶的例子:

class BoilWater extends Thread{       public void run()       {              for(int i=0; i<60; i++)              {                     if(i!=59)                     {                            System.out.println("烧水中...");                     }                     else                     {                            System.out.println("水开了!");                     }              }       }} class DrinkTea{       public static void main(String[] args)       {              BoilWater bw=new BoilWater();              bw.start();               for(int j=0; j<60; j++)              {                     if(bw.isAlive())      //正在烧水                     {                            System.out.println("刷好茶壶,备好茶叶");                     }                     else                     {                            System.out.println("一切就绪,沏茶");                            break;                     }              }       }}   

二. 实现Runnable:

喝茶的例子中,两个线程可以互不干扰执行。现实中我们实现一个任务时,多个人可能使用共同的资源,比如售票。票数是一定的,多个售票员卖的都是这些票中的一部分,你卖得多,她就卖的少。

/*避免了单继承的局限性线程代码在Runnable子类的run方法中*/ //1.实现Runnable接口class Ticket implements Runnable{       private int tickets=100;      //总票数       //2.重写run方法       public void run()       {              while (true)              {                     if (tickets>0)                     {                            System.out.println(Thread.currentThread().getName()+"sale "+tickets--);                     }else                     {                            System.out.println(Thread.currentThread().getName()+"售票结束");                            break;                     }              }       }} class RunnableTest{       public static void main(String[] args)       {              //3.通过Thread类建立线程对象              Ticket tic=new Ticket();              //4.将Runnabel接口的子类对象作为实参传给Thread类构造方法              Thread thr=new Thread(tic);     //第1个售票员              //5.调用Thread类start方法开启线程并调用run方法运行代码              thr.start();               new Thread(tic).start();      //第2个售票员              new Thread(tic).start();      //第3个售票员              new Thread(tic).start();      //第4个售票员       }}   

每个售票员卖完自己的份额,汇报结果。

三. 线程安全:

有一个情况:票卖的差不多了,买票的来了。买票者到一号这晃了晃,一号售票员看了一眼放票的地方,还有一张,然后准备卖出却还没拿到手的时候买票者到二号那里晃了晃;二号售票员也看了一眼,还有一张,也准备卖出而没有拿到手买票者到三号号那里晃了晃;如此也到四号售票员那里晃了晃。这么一来,四个人都卖出了票,结果票多卖出3张,成了负数。实际上100个号的票,不能有0号票。

当多个线程同时操作一个共享数据时,一个线程没有对数据操作完毕,另一个线程抢占了CPU资源对数据进行操作,导致共享数据的错误。

为了再次演示这种情况,我们让售票员看过余票之后打个哈欠。在Ticket类中修改:

class Ticket implements Runnable{       private int tickets=100;      //票数       public void run()       {              while (true)              {                     if (tickets>0)                     {                            try                            {                                   Thread.sleep(10); //让售票员犯困                            }                            catch (Exception e)                            {                                   e.printStackTrace();                            }                            System.out.println(Thread.currentThread().getName()+"sale "+tickets--);                     }else                     {                            System.out.println(Thread.currentThread().getName()+"售票结束");                            break;                     }              }       }}  

1. 同步代码块,

为了防止票被卖成负数的事情发生,我们限制只有每一个售票员的售票行为完成后,才允许另一个售票员卖票行为的开始。Java中通过了一个同步代码块的解决方法,synchronized(); 。synchronized意为同步的。自由是有条件的,同步是在Java同步机制下的同步。将共享数据操作语句用同步代码块包裹,售出负数票的情况就不会再发生。

修改Thicket类:

class Ticket implements Runnable{       private int tickets=100;      //票数       Object obj=new Object();   //监工        public void run()       {              while (true)              {                     //同步代码块,放在离操作共享数据的语句最近的地方                     synchronized (obj)                     {                            if (tickets>0)                            {                                   try                                   {                                          Thread.sleep(10);                                   }                                   catch(Exception e)                                   {                                          e.printStackTrace();                                   }                                   System.out.println(Thread.currentThread().getName()+"sale "+tickets--);                            }else                            {                                   System.out.println(Thread.currentThread().getName()+"售票结束");                                   break;                            }                     }              }       }} 

非常好,尽管打瞌睡,没有卖出0号票,没有出错。

synchronized ( obj ) {

同步代码块;

}

大多数情况下,obj这个对象被翻译为“锁”,哪个线程进入执行代码块就锁上,别的线程就进不来,避免了对共享数据的错误操作。我将之理解为监工,监视4个售票员,允许一号售票员卖票的时候,监工就把其他3个窗口关上,让二、三、四号喝茶去。允许三号卖票的时候,把三号的售票窗口打开,其他的关上。

synchronized,同步。这是有条件的:必须要有超过一个的线程;必须多个线程使用同一个锁(监工);必须保证同步中只有一个线程。在售票的例子中就是:必须不止一个售票员;所有的售票员都被监工管理;监工只能让一个售票员在一段时间里售票。

加了一个监工的确安全了。但是每个售票员都想多卖票,要不就没提成,所以,一号卖票的时候,二三四号就会和监工吵,都想赢得窗口;二号窗口打开的时候,一三四就会和监工干架。在程序中也是如此,每个线程都想拿到锁,以获取CPU资源。跟监工吵架(拿到锁)这个过程中又消耗了资源,这个是同步的弊端。

2. 同步函数,

同步函数将同步声明定义在了函数上,它也有个默认的监工,就是所在的类 this。

class Ticket implements Runnable{       private int tickets=100;      //票数       private boolean over=true;        public void run()       {              while (true)              {                     if (over)                     {                            sale();                     }                     else                     {                            break;                     }              }       }        //同步函数       public synchronized void sale ()       {              if (tickets>0)              {                     try                     {                            Thread.sleep(10);                     }                     catch (Exception e)                     {                            e.printStackTrace();                     }                     System.out.println(Thread.currentThread().getName()+"sale "+tickets--);              }else              {                     System.out.println(Thread.currentThread().getName()+"售票结束");                     over=false;              }       }}   

3. 我们让同步代码块在默认窗口售票,让同步函数在新窗口售票,

1)一般情况:

class Ticket implements Runnable{       private int tickets=100;      //票数       public boolean onlyWin=true;   //仅仅使用默认售票窗口       private boolean fucSyn=true;    //防止同步函数中同步线程死循环        public void run()       {              if (onlyWin)              {                     while (true)                     {                            //同步语句块,使用监工this。                            synchronized (this)                            {                                   if (tickets>0)                                   {                                          System.out.println(Thread.currentThread().getName()+" 默认窗口售票 "+tickets--);                                   }else                                   {                                          System.out.println(Thread.currentThread().getName()+"售票结束");                                          break;                                   }                            }                     }              }else              {                     while (true)                     {                            if (fucSyn)                            {                                   sale();                            }                            else                            {                                   break;                            }                     }              }       }        //同步函数。监工默认为this,和默认售票窗口的监工是同一个。       public synchronized void sale ()       {              if (tickets>0)              {                     System.out.println(Thread.currentThread().getName()+" 新开窗口售票 "+tickets--);              }else              {                     System.out.println(Thread.currentThread().getName()+"售票结束");                     fucSyn=false;              }       }} class RunnableTest{       public static void main(String[] args)       {              Tickettic=new Ticket();               new Thread(tic).start();      //第1个售票员              new Thread(tic).start();      //第2个售票员              //主线程有最高优先权,以上两条线程开启后处于临时状态,并非立刻执行。              try{Thread.sleep(10);}catch(Exception e){e.printStackTrace();}              tic.onlyWin=false;        //开启新窗口              new Thread(tic).start();      //第3个售票员              new Thread(tic).start();      //第4个售票员       }}   

为什么只有三个?只要票没卖错就成。

2)同步函数为静态的:

当同步函数为静态的,则监工不再是this,而是函数所属的类。所以相应的要把同步代码块的监工设为这个类.class。

四. 单例设计模式:

//懒汉式单例设计模式class Single implements Runnable{       private static Single s = null;        public static Single getInstance()       {              if (s==null)              {                     synchronized(Single.class)                     {                            if (s==null)                            {                                   s = newSingle();                                   System.out.println("对象已有");                            }                     }              }              return s;       }        public void run ()       {              while (true)              {                     if (s==null)                     {                            getInstance();                     }                     else                     {                            break;                     }              }       }} class SingleTest{       public static void main(String[] args)       {              Single single=new Single();              //single.run();               new Thread(single).start();              new Thread(single).start();              new Thread(single).start();              new Thread(single).start();       }}   /*//饿汉式 class Single{       private static final Single s = Single();             public static Single getInstance()       {              return s;       }} */  

五. 死锁:

嵌套同步的时候,要么和谐,要么死锁,

class Locks implements Runnable{       private boolean lock;        Locks (boolean lock)       {              this.lock=lock;       }             public void run()       {              if (lock)              {                     while (true)    //一定可以遇到死锁的情况                     {                            synchronized(MyLock.obj1)                            {                                   System.out.println("ifMyLock.obj1");                                   synchronized(MyLock.obj2)                                   {                                          System.out.println("ifMyLock.obj2");                                   }                            }                     }              }else              {                     while (true)    //一定可以遇到死锁的情况                     {                            synchronized(MyLock.obj2)                            {                                   System.out.println("elseMyLock.obj2");                                   synchronized(MyLock.obj1)                                   {                                          System.out.println("elseMyLock.obj1");                                   }                            }                     }              }       }} class MyLock{       static Object obj1=new Object();       static Object obj2=new Object();} class DeadLockTest{       public static void main(String[] args)       {              Thread th1=new Thread(newLocks(true));              Thread th2=new Thread(newLocks(false));              th1.start();              th2.start();       }}   

光标一直在闪。程序无法继续,线程被锁死。

烦恼忧愁不用追,吃点好的别嫌贵,

Java上路09

相关文章:

你感兴趣的文章:

标签云: