现在的位置: 首页 > 综合 > 正文

黑马程序员_【java】多线程

2014年09月05日 ⁄ 综合 ⁄ 共 5614字 ⁄ 字号 评论关闭

----------------------
android培训
java培训、期待与您交流! ----------------------

知识点:进程,线程,多线程创建方式,同步函数,同步代码块,死锁

 

进程:一个正在执行中的程序

每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

线程:是进程中的一个独立的控制单元,线程控制着进程的执行。

注:一个进程中至少有一个线程(主线程)。

 java VM启动的时候会有一个进程java.exe,该进程中至少有一个线程负责java程序的执行。

而且这个线程运行的代码存在于main方法中。

该线程称之为主线程。

1.如何在自定义的代码中,自定义一个线程呢?

创建线程的第一种方式:继承Thread

步骤:

1.定义类继承Thread

2.复写Thread类中的run方法

        
目的:将自定义的代码存储在run方法中,让线程运行。

3.调用线程的start方法

        
该方法有两个作用:启动线程,调用run方法。

示例代码:

class Demo extends Thread  //Demo类继承Thread类

{

         publicvoid run()     //复写Thread类中的run方法

         {

                   for(inti = 0 ;i<40;i++)

                            System.out.println("demorun"+i);

         }

}

class ThreadDemo

{

         publicstatic void main(String[] args)

         {

                   Demod = new Demo();//创建好一个线程

                   d.start();//开启线程并执行该线程的run方法

                   

                   for(inti = 0 ;i<40;i++)

                            System.out.println("adsfa"+i);

         }

}

发现运行结果每一次都不同,因为多个线程都获取cpu执行权cpu执行到谁,谁就运行。

明确一点:在某一个时刻,只能有一个程序(线程)在运行,多核除外

cpu在做着快速的切换,以达到看上去是同时运行的效果。

我们可以形象的把多线程的运行行为在互相抢夺cpu的执行权

 这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多长时间,cpu说了算。

为什么要覆盖run方法呢?

Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法,也就是说Thread类中的run方法,用于存储线程要运行的代码

 举例:

创建两个线程,和主线程交替运行

线程都有自己默认的名称:Thread-编号 该编号从0开始

 static Thread currentThread(); //获取当前线程对象的方法。

getName();//获取线程名称的方法。

设置线程名称:setName()或者通过构造函数

示例代码:

class Test extends Thread

{

         publicTest(String name)

         {

                   super(name);//通过构造函数给线程取名。

         }

         publicvoid run(){

                   for(intx = 0;x <10;x++)

                   {

                   System.out.println((Thread.currentThread()==this)+"...."+this.getName()+"run..."+x);

                   }

         }

}

class ThreadTest

{

         publicstatic void main(String[] args)

         {

                   Testt1 = new Test("one");  //创建一个名称为 one的线程

                   Testt2 = new Test("two"); //创建一个名称为 two的线程

                   t1.start();  //启动线程t1

                   t2.start();//启动线程t2

                   for(inti = 0 ;i<20;i++)

                            System.out.println("helloworld"+i);

         }

}

线程运行状态图:

创建线程的第二种方式:实现Runable接口

步骤:

1.定义类实现Runnable接口

2.覆盖Runnable接口中的run方法,将线程要运行的代码存放在该run方法中。

3.通过Thread类建立线程对象

4.Runnable接口的子类对象作为实际参数传递给Thread类的构造函数

        
为什么要将Runnable接口的子类对象传递给Thread的构造函数?

        
因为,自定义的run方法所属的对象是Runnable接口的子类对象,所以

        
要让线程去执行指定对象的run方法。就必须明确该run方法所属的对象。

5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

 

示例代码:(多窗口卖票)

class Ticket implements Runnable  //定义类实现Runnable接口

{

         privateint tick = 100;  //定义票总数

         publicvoid run()  //覆盖Runnable接口中的run方法

         {

                   while(true)

                   {

                            if(tick>0)

                            {

                                     System.out.println(Thread.currentThread().getName()+"sale:"+tick--);

                            }

                   }

         }

}

class TicketDemo

{

         publicstatic void main(String[] args)

         {

                   Tickett = new Ticket(); //创建Runnable接口的子类对象

 

Thread t1 =new Thread(t);// 通过Thread类建立线程对象,并将Runnable接口的子

//类对象作为实际参数传递给Thread类的构造函数

                   Threadt2 = new Thread(t);

                   t1.start();//用Thread类的start方法开启线程并调用Runnable接口子类的run方法

                   t2.start();

 

         }

}

实现Runnable接口的方式和继承Thread类的方式的区别?

实现方式好处:避免了单继承的局限性。

建议:在定义线程时,最好使用实现方式(第二种创建线程的方式)

继承Thread类:线程代码存放在Thread子类的run方法中。

实现Runnable接口:线程代码存放在接口的子类的run方法中。

 

通过分析上面的例子,发现打印出0-1-2等错票,即多线程的运行出现了安全问题。

问题产生的原因:

当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,

还没有执行完,另一个线程就参与进行执行,导致共享数据的错误。

解决办法:

对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他

线程不可以参与执行。

java对于多线程的安全问题提供了专业的解决方式,就是--同步代码块

格式:

synchronized(对象)    

{

         需要被同步的代码

}

对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程,即使获取了cpu的执行权,也进不去,因为没有获取锁。

 同步的前提(重要):

1.必须要有两个或者两个以上的线程

2.必须是多个线程使用同一个锁。

必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题

弊端:多个线程需要判断锁,较为消耗资源。

举例代码:多窗口卖票案例

class Ticket implements Runnable

{

         privateint tick = 50;

         Objectobj = new Object();

public voidrun()

         {

                   while(true)

                   {

                            synchronized(obj)  //obj即是锁

                            {

                                     if(tick>0)

                                     {

                                               try

                                               {

                                                        Thread.sleep(5);

                                               }

                                               catch(Exception e)

                                               {

                                               }

                                               System.out.println(Thread.currentThread().getName()+"sale:"+tick--);

                                     }

                            }

                   }

         }

}

 

class TicketDemo2

{

         publicstatic void main(String[] args)

         {

                   

                   Tickett = new Ticket();

 

                   Threadt1 = new Thread(t);

                   Threadt2 = new Thread(t);

                   Threadt3 = new Thread(t);

                   Threadt4 = new Thread(t);

                   t1.start();

                   t2.start();

                   t3.start();

                   t4.start();

 

         }

}

多线程安全问题举例:

需求:银行有一个金库,有两个储户分别存300元,每次存100,存3

目的:该程序是否有安全问题,如果有,如何解决?

 如何找问题?

1.明确哪些是多线程运行代码。

2.明确共享数据是哪些

3.明确多线程运行代码中哪些语句是操作共享数据的。

示例代码:

class Bank

{

         privateint sum;

       publicsynchronized void add(int n)  //synchronized作为修饰符修饰函数 即同步有两种表现形式,同步代码块和同步函数

         {

                            sum= sum + n;

                            try{Thread.sleep(10);}catch(Exceptione){}

                            System.out.println("sum="+sum);

         }

}

class Cus implements Runnable

{

         privateBank b = new Bank();

         publicvoid run()

         {

                   for(intx = 0 ;x<3;x++)

                   {

                            b.add(100);

                   }

         }

}

class BankDemo

{

         publicstatic void main(String[] args)

         {

                   Cusc = new Cus();

                   Threadt1 = new Thread(c);

                   Threadt2 = new Thread(c);

                   t1.start();

                   t2.start();

         }

}

如果同步函数被静态修饰后,使用的锁是什么呢?

 通过验证,发现不再是this,因为静态方法中也不可以定义this

静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象

类名.class,该对象的类型是Class

静态的同步方法中,使用的锁是该方法所在类的字节码文件对象。类名.class

示例代码:

 

class Ticket implements Runnable

{

         privatestatic int tick = 1550;

         booleanflag = true;

         publicvoid run()

         {

                   if(flag){

                            while(true)

                            {

                                     synchronized(Ticket.class)  //锁为字节码文件对象,因为它在内存中唯一

{

                                               if(tick>0)

                                               {

                                                        try

                                                        {

                                                                 Thread.sleep(5);

                                                        }catch(Exception e)

                                                        {}

                                                        System.out.println(Thread.currentThread().getName()+"code---:"+tick--);

                                               }       

                                     }

                            }

                   }

                   else

                            while(true)

                                     show();

         }

         publicstatic synchronized void show()

         {

                   if(tick>0)

                   {

                            try

                            {

                                     Thread.sleep(5);

                            }catch(Exception e)

                            {}

                   System.out.println(Thread.currentThread().getName()+"show------:"+tick--);

                   }

         }

}

class StaticMethodDemo

{

         publicstatic void main(String[] args)

         {

                   Tickett = new Ticket();

                   Threadt1 = new Thread(t);

                   Threadt2 = new Thread(t);

                   t1.start();

                   try

                   {

                            Thread.sleep(10);

                   }

                   catch(Exception e)

                   {

                   }

                   t.flag= false;

                   t2.start();

         }

}

延时加载的单例设计模式示例:懒汉式  

示例代码:

class Single

{

         privatestatic Single s = null;

         privateSingle(){}

public static Single getInstance()//加了同步,比较低效。

         {

                   if(s== null)

                   {

                            synchronized(Single.class)//防多线程

                            {

                                     if(s== null)

                                               s= new Single();

                            }

                   }

                   returns;           

         }

}

死锁:同步中嵌套同步

死锁程序经典案例:

示例代码:

class Test implements Runnable

{

         privateboolean flag;

         Test(booleanflag)

         {

                   this.flag= flag;

         }

         publicvoid run()

         {

                   if(flag)

                   {

                            while(true)

                            {

                                     synchronized(MyLock.locka)

                                     {

                                               System.out.println("iflocka");

                                               synchronized(MyLock.lockb)

                                               {

                                                        System.out.println("iflockb");

                                               }

                                     }

                            }

                   }

                   else

                   {

                            while(true)

                            {

                                     synchronized(MyLock.lockb)

                                     {

                                               System.out.println("elselockb");

                                               synchronized(MyLock.locka)

                                               {

                                                        System.out.println("elselocka");

                                               }

                                     }

                            }

                   }

         }

}

class MyLock

{

         staticObject locka = new Object();

         staticObject lockb = new Object();

}

class DeadLockTest

{

         publicstatic void main(String[] args)

         {

                   Threadt1= new Thread(new Test(true));

                   Threadt2= new Thread(new Test(false));

                   t1.start();

                   t2.start();

         }

}

 

 

----------------------
android培训
java培训、期待与您交流! ----------------------

抱歉!评论已关闭.