多线程-------java版

来源:互联网 发布:mac上的windows虚拟机 编辑:程序博客网 时间:2024/04/28 21:47

一、      概念

线程是什么?

         线程是一个进程的执行路径。多线程当然就是一个进行的多条执行路径。

         什么是进程?

                  进程是一个程序的一个事例。

         什么又是程序?

                  程序就是有组织的代码组成可以运行的文件。如.exe文件.class文件

为什么需要多线程?

         当一个进程被阻塞的时候,我们需要另外一个进程来执行,这样就能提高cpu的利用率,让用户看起来跟并行运行一样。但是开辟一个新的进程需要维护很多资源。进城之间相互切换也是比较复杂。而且操作系统也不允许进程之间相互访问内存。也不能让他们相互访问内存,这样就会导致程序的紊乱。这时候就需要有多线程的出现。在一个进程中创建多个线程,线程之间切换消耗较小,而且共享内存区域。

         总结一句:线程就是进程中不同的执行路径。

二、多线程的创建和启动:

Java为例:

一、首先需要有这个路径。

1.1、在java中线程类Thread。因此我们需要继承Thread类来让我们需要的类有开辟路径的功能。

         class  MyThread extends Thread{};

        1.2、有了路径以后我们就需要写我们要在这个路径中做什么事情,比如说打印1100,或者取余数等等操作。要重写Thread类中的public void run(){} 方法。

    1.3、有了路径、有了能干的事,接下来我们就要在使用他的地方开启他就可以了。

        MyThread  myThread=new MyThread();

        myThread.start();

        这样当程序运行到myThread.start()。这里的时候操作系统就会知道你这里开辟了一个新的线程。等cpu有空的时候给你执行。注意:这里并不是立刻就执行自己创建的线程,而是等着,cpu给你分配“时间片“才能执行。

    其实这句话应该理解为:操作系统告诉cpu这里有一个新的线程,你什么时候有空了就执行他一下。如果cpu没空也是不会执行新线程的,然后就执行run()方法。

                  如果直接调用MyThread类的run()方法,这叫做方法调用,而不是开启新线程。方法调用不会告诉cpu我这里有一个新的线程等着你执行。

二、2.1、建立路径:因为我们的目标是执行东西,干事情,因此我们并需要继承Thread类,实际上需要他的run()方法,那么第二种方式就是继承接口 Runnable,它里面只有一个run()方法。 Class MyThread implements Runnable{}.

2.2Runnable接口中只有run()方法,这样我们重写run()方法即可。

Public voidrun(){};

2.3、有了路径有了事情,我们就需要开启路径了。也是start().方法。因为我们MyThead类中并没有start()方法,并不能没有通知cpu的功能。因此我们需要一个Thread一个对象。把该对象和继承Runnable接口对象相关联。

Thread t=newThread(MyThread);

t.start()。同样开辟并启动了一个新的线程。

三、Thread类的几个方法

线程启动以后我们需要控制线程,不能任由“它们”自由发挥。

线程的状态:

1.1、       就绪状态。此状态就是刚刚start()以后,可以随时等候cpu干活。

1.2、       运行状态。

1.3、       阻塞状态。当在运行之中产生了阻塞事件,那么就会从运行状态到阻塞状态。

当然还有创建和结束状态。

方法1:操作线程最多的是sleep().方法,该方法是一个静态方法。Thread.sleep()。参数为毫秒。1000毫秒=1秒。

sleep()的时候可能被其他的线程所打扰,因此可能会产生“IntertuptedException”异常,这里最好用try catch一下。要不然会编译出错。

         这里注意sleep进入的是“阻塞状态”。而并不是就绪状态。等他睡醒以后才是“就绪状态”

方法2 boolean isAlive()。非静态方法,判断线程是不是还“活着”。活着的状态有三种:就绪、运行、阻塞。

 void join()。非静态方法。当调用某一个Thread对象的join方法就会把该对象的方法加入到调用该对象start()方法的线程中去运行,相当于了。方法调用,其实执行的过程就是类似于方法调用,必须等了join方法的线程执行完了,它依附的主线程才能执行。同样会抛出异常。

方法3yield()方法,同样是静态方法。“高风亮节”。暂停当前的线程。它并不是进入“阻塞状态”。而是进入了就绪状态。它只是暂停一下,让出现在时间片。

 

注意这里都是Thread的方法,如果是从Runnable接口继承下来的线程是不能有上述的方法的。使用方法要分得清楚方法是那个类。

 

三、       前面提到,多线程可以共享一块内存,这就是产生了线程同步的问题。

线程同步是一个什么问题呢?

这里用一个例子说明:有个银行账户:里面有3000块。账户有存折和银行卡两种方式可以取钱。先用存折到柜台去取钱的时候,前台查看账户确实有3000块钱,已经出去2000块,还没有把账户余额改成1000,交到储户手里的时候,在拿着银行卡在自动取款机上取钱,同样取2000,自动取款机查看有3000块钱,这就把钱吐给你,并且把账户改成1000。而在柜台同样又开始进行,也把钱给你,把账户余额改成1000.这就取出了4000块。

这就是多线程带来最直接的问题。因为多线程可以同时访问同一块内存,也就是都能取钱。只是在其中一个取钱的时候,另一个线程打断了它。导致问题产生。而又不能不让其他线程使用。这样怎么办呢?

最简单的思路就是,我在取钱的时候别人不能动取钱的操作。等我取完钱了其他人才能有这个操作。也就是加一把锁子。

有两种方法:

1、  synchronized(this){}给当前操作次对象的操作加一个把锁。参数也可以使其他的对象。但是就是给其他的对象加锁了。

2、  另一个就是在方法前加上synchronized,当某一个线程在执行的时候其他线程是不进来的。

成这样的问题产生其实也可以说是,把本来不能再分的代码,被分开了。把这些代码用synchronized (this)括起来就行了。

四、       在三中提到了线程同步问题。那么线程同步真的就安全了吗?

答案当时是否定的,因为这样还会带来“死锁”的问题。

下面介绍“死锁”是怎么产生的。

线程A B。对象 ab

执行线程A的时候需要把ab对象锁住、执行线程B的线程时候需要把ba线程锁住。但是在执行A线程的锁a后线程被打断,执行了线程B。线程B执行到锁b,等着A线程执行完成使用a对象,而A线程同样等着B线程执行完成使用b线程。最后两个线程谁都不放,导致了线程的“死锁”。

--------------------------------------------------------------例子----------------------------------------

 

线程:一个程序中的不同执行路径。在一个程序中执行路径有多少条,过去写的都是一条执行路径,无论是调用方法,还是new一个类,都是一条执行路径main

         进程:机器上个一个.class文件.exe文件都是一个进程。进程就是一个程序的一个事例。

         cpu把时间分成时间片,在同一个时间点上只有一个线程在执行。速度快,逻辑上是并行,物理上仍然是只执行一个线程。

        

         既然线程是程序的不同执行路径,那么怎么开辟一条新的路径?让一个新的路径执行起来?

                   java中多线程是同过java.lang.Thread类来实现的。每一个Thread类的对象就代表一个新的线程。有了新的线程那么必须有东西执行,在线程类的run方法中写

                   要执行的代码。写完之后那么就表明已经新的路径已经开辟好了。要想让它走起来,还要通过Thread对象的start()方法来启动线程。

                  

                   为什么不直接调用run()方法呢?其实这就是开启线程和函数调用的区别。Thread类的start()方法告诉CPU“我现在有一个新的的线程,看你什么时候有时间给我分配点时间片过来”。

                   而直接写thread.run()(假设Threadthread=new Thread();)那么并没有创建线程。

                  

                   创建线程的两种方法:

                   一、

                                     1、写一个类来继承接口Runnable告诉编译器这是一个线程类。 class A implements Runnable{}

                                     2、实现接口Runnable里面的方法run(),把新线程的功能写进去public void run(){}

                                     3、创建一个线程对象来关联A(A a=newA())。因为A类中没有start()方法,只有start()方法才能通知CPU这里有个新线程。Thread t =new Thread(a);

                                     4、用start()方法开启新的线程。t.start();

                   二、

                                     1、既然必须要用到start()方法,那么我们就直接继承Thread类。  class B extends Thread{}

                                     2、同样实现类的publicvoid run()方法

                                     3Thread对象开启。Thread  t=new Thread(); t.start();

                                    

                   无论是那种方法新线程的“分叉”都是从start()方法开始的,因为只有start()后cpu才会开始给新的线程“时间片”执行。

 

*/

 

public class TestThread1

{

                   publicstatic void main(String [] args)

                   {

                                     Runner1r=new Runner1();

                                     Threadt=new Thread(r);

                                     t.start();

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

                                     {

                                                        System.out.println("MainThread:---------"+i);

                                               }

                            }

         }

        

         classRunner1 implements Runnable

         {

                            publicvoid run()

                            {

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

                                               {

                                                                 System.out.println("Runner1Thread:---------"+i);

                                                        }

                                     }

                   }

Sleep的使用:

/*Thread中的sleep方法是一个静态方法,用来让当前运行的进程“休眠”一段时间,参数是毫秒,1000毫秒就是1秒。

         但是sleep()的使用是会抛出异常的,throwInterruptedException。而通常又是在run)方法中使用Thread.sleep方法。而run()方法和runnable接口并没有抛出此类异常。

         子类不能抛出父类不曾有的异常的原则,因此不能用throws InterruptedException。必须用try catch来捕获此类异常。

         线程类有一个方法叫interrupt()。是用来中断此线程的,比stop稍微“温柔”一点。但是同样不提倡使用。这里需要注意interrupt()方法并不是静态方法,因此需要类对象来调用。

*/

import java.util.*;

class MyThread implements Runnable

{

                   publicvoid run()

                   {

                                     while(true)

                                     {

                                                        System.out.println("--------"+newDate()+"-----");

                                                                 try

                                                                 {

                                                                           Thread.sleep(1000);

                                                                 }catch(InterruptedExceptione)

                                                                 {

                                                                                    return;

                                                                 }

                                     }

                   }

}

public class TestSleep

{

         publicstatic void main(String [] args)

         {

                   MyThreadth=new MyThread();

                   Threadthread=new Thread(th);

                   thread.start();

                   try

                   {

                            Thread.sleep(10000);

                   }        catch(InterruptedException e)

                   {

                           

                   }

                   thread.interrupt();

         }

}

Thread  join()

/*

         threadjoin()方法的使用,在runnable接口中并没有join()方法和getName()方法,runnable里面只有run()方法。如果想要使用除了

         run()以外得方法,我们自己定义的线程类只能是从Thread继承过来的。只有这样的子类才有Thread的里面的方法。

         join方法是这样运行的,需要加入的现成对象名.join().表示加入到当前的线程前执行。相当于方法的调用。在下面的程序中会看到先执行

         MyThread线程以后在执行Main现成,这是应为调用了thread.join()方法。由“分叉”回到一个枝。

 

*/

class MyThread extends Thread

{

                   MyThread(Strings)

                   {

                                     super(s);

                   }       

                   publicvoid run()

                   {

                                     for(inti=1;i<=10;i++){

                                                        System.out.println("------iam:"+getName()+"-----");

                                                        try

                                                        {

                                                                           Thread.sleep(1000);

                                              

                                                                 }catch(InterruptedExceptione)

                                                                 {

                                                                                    return;

                                                                           }

                                               }

                            }

}

public class TestJoin

{

                   publicstatic void main(String [] args)

                   {

                                     MyThreadthread=new MyThread("abc");

                                     thread.start();

                                     try{

                                               thread.join();

                                     }catch(InterruptedExceptione)

                                               {

                                                       

                                                        }

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

                                                        {

                                                                           System.out.println("mainthread is run……");

                                                                 }

                                              

                            }

         }

 

Yield 静态方法,让出一下CPU。“高风亮节

class MyThread extends Thread

{

                   MyThread(Strings)

                   {

                                     super(s);

                   }

                   publicvoid run()

                   {

                            for(inti=1;i<=100;i++)

                            {

                                               System.out.println("-----"+getName()+"---"+i);

                                               if(i%10==0)

                                               {

                                                                 Thread.yield();

                                                                 System.out.println("===="+getName()+"让了");

                                               }

                            }

                   }

}

public class TestYield

{

                   publicstatic void main(String [] args)

                   {

                                     MyThreadth1=new MyThread("郭子");

                                     MyThreadth2=new MyThread("");

                                     th1.start();

                                     th2.start();

                                    

                            }

         }

Priority

/*不能把代码直接写到类里面,这样会提示     需要<标识符>

         关于优先级的问题setPriority(inti);

*/

class MyThread implements Runnable

{

//               MyThread(Strings)

//               {

//                                  super(s);

//                         }

                            publicvoid run()

                            {

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

                                               {

                                                                 System.out.println("t1"+"---"+i);

                                                        }

                                     }

         }

         classMyThread2 implements Runnable

{

//               MyThread(Strings)

//               {

//                                  super(s);

//                         }

                            publicvoid run()

                            {

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

                                               {

                                                                 System.out.println("t2"+"-----"+i);

                                                        }

                                     }

         }

         publicclass TestPriority

         {

                   publicstatic void main(String [] args)

                   {

                                     MyThreadt1=new MyThread();

                                     MyThread2t2=new MyThread2();

                                     Threadtt1=new Thread(t1);

                                     Threadtt2=new Thread(t2);

                                     tt1.setPriority(Thread.NORM_PRIORITY+3);

                                     tt1.start();

                            tt2.start();

                   }

         }

正确关闭线程的方法:

/*正确的关闭线程的方法

         正确关闭线程的方法不是用 线程类的对象名.interrupet()方法,当然这样是可以停止线程的。但是太粗暴了,而是在线程类中增加一个布尔类型的标记,当这个标记是true的时候执行

         当然这个标记是私有的,类的外部暴露一个共有的shutdown方法,来设置标记的true或者false

*/

class MyThread implements Runnable

{

                   privateboolean flag=true;

                   publicvoid run()

                   {

                                     inti=1;

                                     while(true)

                                     {

                                                       

                                                        if(flag==false)

                                                        {

                                                                 System.out.println("MyThreadis over");

                                                                           break;

                                                        }                          

                                                        System.out.println("MyThreadis running "+i++);                    

                                     }                          

                   }

                   publicvoid shutdown()

                   {

                                     flag=false;

                   }

}

public class TestThread

{

         publicstatic void main(String [] args)

         {

                   MyThreadthread=new MyThread();

                   Threadth=new Thread(thread);

                   th.start();

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

                   {

                                     System.out.println("mainthread is running......."+(i+1));

                            }

                   System.out.println("主线程结束");

                   thread.shutdown();

                   }

         }

线程同步:

/*两个线程同时访问Timer对象的add方法,当访问的时候就会出现t1现在是第num次访问

 

                   运行结果:t12次访问Timer

                                       t22次访问Timer

                                                                

分析结果:这是因为在第一个线程运行到Thread.sleep(1)的时候就停了1毫秒,这样就把执行权利交给了t2。此时的num已经++ num=1。而t2运行到Thread.sleep(1)的时候同样停了1毫秒,这样就把执行全力交给了t1。但是这个时候的num++  num=2.了。所以才会出现都是第2次访问。

                                                                

         */

 /*class Timer

{

                   privatestatic int num=0;

                   publicvoid add(String name)

                   {

                                     num++;

                                     try

                                     {

                                                        Thread.sleep(1);

                                                       

                                     }catch(InterruptedExceptione)

                                     {}

                                     System.out.println(name+"你是第"+num+"次访问Timer");

                            }

 

         }

public class TestSync implements Runnable

{

         Timer  timer=new Timer();

         publicstatic void main(String [] args)

         {

                   TestSyncthread=new TestSync();

                   Threadth1=new Thread(thread);

                   Threadth2=new Thread(thread);

                   th1.setName("t1");

                   th2.setName("t2");

                   th1.start();

                   th2.start();

                           

                   }

                   publicvoid run()

                   {

                                     timer.add(Thread.currentThread().getName());

                            }

         }*/

class Timer

{

         privatestatic int num=0;

         publicvoid add(String name)

         {

                            num++;

                            try

                            {

                                               Thread.sleep(1);

                                     }catch(InterruptedExceptione)

                                     {}

                                     System.out.println(name+""+num+"次访问Timer");

                   }                

}       

class MyThread implements Runnable

{

                   Timertimer=new Timer();

                   publicvoid run()

                   {

                                     timer.add(Thread.currentThread().getName());

                   }

                   publicstatic void main(String [] args)

                   {

                                     MyThreadthread=new MyThread();

                                     Threadt1=new Thread(thread);

                                     Threadt2=new Thread(thread);

                                     t1.setName("guozijie");

                                     t2.setName("duanran");

                                     t1.start();

                                     t2.start();

                            }

         }

      写一个“死锁

/*MyDeadLock.java------------死锁的演示

         多线程的产生就可能会引起死锁,A线程在执行的过程中,为了防止实现线程同步,需要锁定a对象,在执行a锁内中还需要锁定b对象。a锁才能运行结束。

                                                                                                                                              B线程在执行过程中,需要锁定b对象,在执行b锁还需要锁定a对象才能执行结束。而这时候a对象正在被A线程锁着呢,导致了B线程进不去,只能等着A线程用完a对象里面锁。

                                                                                                                                             同样A也在等着B的把b对象的锁子用完。产生"死锁".

*/

public class MyDeadLock implements Runnable

{

         publicint flag=1;

         publicstatic Object o1=new Object();

         publicstatic Object o2=new Object();

         publicvoid run()

         {

                            System.out.println("当前线程:"+Thread.currentThread().getName());

                            if(flag==1)

                            {       

                                               synchronized(o1)

                                               {

                                                        System.out.println("接下来我需要o2锁子");

                                                        try

                                                                 {

                                                                                    Thread.sleep(300);                                                                           

                                                                 }catch(InterruptedExceptione)

                                                                 {

                                                                                   

                                                                 }

                                                                 synchronized(o2)

                                                                 {

                                                                                    System.out.println("两个锁子flag="+flag);

                                                                 }

                                               }

                            }

                           

                            if(flag==0)

                            {

                                               synchronized(o2)

                                               {

                                                                 //在这里停一下,让打断这个线程

                                                                 System.out.println("接下来我需要01锁子");

                                                                 try

                                                                 {

                                                                                    Thread.sleep(300);

                                                                          

                                                                 }catch(InterruptedExceptione)

                                                                 {

                                                                                   

                                                                 }

                                                                 synchronized(o1)

                                                                 {

                                                                                    System.out.println("两个锁子flag="+flag);

                                                                 }

                                               }

                            }

                           

         }

         publicstatic void main(String [] args)

         {

                            MyDeadLocklock1=new MyDeadLock();

                                     MyDeadLocklock2=new MyDeadLock();

                            Threadth1=new Thread(lock1);

                            Threadth2=new Thread(lock2);

                            th1.setName("t1");

                            th2.setName("t2");

                            lock1.flag=1;

                            lock2.flag=0;

                            th1.start();

                            th2.start();

                   }

        

}

一道面试题:

/*

         在下面的程序中确实把方法2s锁定了,把它里面的的内容都锁定了,但是同样m2并没有锁定,其他的线程还是可以访问b

         因此一下输出是这是m2()方法的b=1000;

                                                                  这是m1()方法的b=1000

 

设计一个同步的对象的时候,比较麻烦,掌握的原则的是只要需要改里面对象的值的函数就需要加上锁子。只读的对象就不需要加锁子了。

 

*/

 

public class TT implements Runnable

{

         intb=100;

         publicsynchronized  void  m1()

         {

                            //synchronized(this){

                            b=1000;

                            try

                            {

                            Thread.sleep(5000);

                            }

                            catch(Exceptione)

                            {

                                               e.printStackTrace();

                            }

                            System.out.println("这是m1方法的b="+b);

                   //}

         }

         publicvoid m2()

         {

                            System.out.println("这是m2()方法的b="+b);

                   }

         publicvoid run()

         {

                            m1();

                   }

                   publicstatic void main(String [] args)

                   {

                                     TTtt=new TT();

                                     Threadt=new Thread(tt);

                                     t.start();

                                     /*

                                     try

                                     {

                                     Thread.sleep(1000);

                                     }catch(Exceptione)

                                     {}*/

                                     tt.m2();

                            }

}

 

 

 

0 0
原创粉丝点击