读书人

多线程小结

发布时间: 2012-09-09 09:27:54 作者: rapoo

多线程总结


多线程:
?线程是进程中的一个控制单元。
?一个进程至少有一个线程,为了提高效率,可以在一个进程中开启多个控制单元。
?并发运行。如:多线程下载软件。
?
?可以完成同时运行,但是通过程序运行的结果发现,虽然同时运行,但是每一次结果都不一致。


?因为多线程存在一个特性:随机性。
?造成的原因:CPU在瞬间不断切换去处理各个线程导致的。
?可以理解成多个线程在抢cpu资源。

?如果在程序中创建一个新的控制单元呢?

?Java对线程有自己描述,而且在调用系统资源。
?该描述的类是Thread。只要创建Thread类的对象即可在内存中建立新的控制单元。

?但是创建线程的目的是为了运行自定义的代码。
?自定义的代码如何和多线程相结合呢?
?通过start方法明确,线程是在执行run方法。
?那么只要将线程需要运行的代码存入到run方法中即可。
?只要继承Thread类并复写该run方法即可。

?创建线程的第一种方式:继承Thread类;
?1,定义Thread类的子类。创建子类对象,就是在内存中建立了控制单元。
?2,复写Thread类中的run方法。将多线程要运行的代码存入其中。
?3,调用线程Thread中的start方法开启线程,并让jvm运行run方法。

?线程的四种状态:
?1,被创建.new Thread类或者Thread类的子类对象。
?2,通过start方法,让线程进入到了运行状态。
?3,线程存活的情况,可以不运行,因为到了冻结状态。
?4,消亡,线程执行的代码结束。

?如何获取当前线程对象呢?
?通过Thread类中的静态方法:Thread currentThread()。
?获取线程的名称 getName():默认是Thread-编号。

?class Demo extends Thread
?{
??public void run()
??{
???for(int x=0; x<20; x++)
???{
????System.out.println(Thread.currentThread().getName());
???}
??}
?}

?main()
?{
??Demo d1 = new Demo();
??Demo d2 = new Demo();
??//d1.run();
??//d2.run();当直接对象调用run方法,其实是一共有三个线程,
???//但是只有主线程在运行。所以打印的都是主线程的名称main。
??d1.start();
??d2.start();//有三个线程,并三个线程都处于运行状态,在互相抢资源运行。


??for(int x=0; x<30; x++)
??{
???System.out.println(Thread.currentThread().getName());
??}
?}

?class Ticket extends Thread
?{
??private int num = 100;
??public void run()
??{
???while(true)
???{
????if(num>0)
????{
?????System.out.println(Thread.currentThread().getName()+"..."+num--);
????}
???}
??}
?}
?main()
?{
??Ticket t1 = new Ticket();
??Ticket t2 = new Ticket();
??Ticket t3 = new Ticket();
??Ticket t4 = new Ticket();
??t1.start();
??t2.start();
??t3.start();
??t4.start();

??//结果是将票进行打印,但是出现了400张票。
??因为每一个ticket对象中都有自己的num。
??可以通过将num修饰成static的,这是可以的,但是通常不建议定义static因为生存周期过长。


??Ticket t1 = new Ticket();
??
??t1.start();
??t1.start();
??t1.start();
??t1.start();

??//一个线程开启多次,会造成无效的线程状态异常。IllegalThreadStateException


?}


?什么时候需要多线程呢?
?当多部分代码需要同时运行时,就需要使用多线程技术。
?聊天软件的例子。发送区域和接收数据区域就是同时运行的。

------------------------------------

两种创建方式:
?1,继承Thread类。复写run方法,通过start方法开启线程。并让jvm调用run方法。
?2,实现Runnable接口;
??2.1定义类实现Runnable接口。
??2.2复写接口中run方法。
??2.3通过Thread类建立线程对象。
??2.4将实现了Runnable接口的子类对象作为参数传递给Thread类的构造函数。
???目的:让线程可以执行执行的接口子类的run方法。
??2.5调用Thread类的start方法开启线程。
?
?这两种方式区别:
?线程代码存放的位置不同,一个存放在Thread类的子类中。一个存在Runnable接口的子类中。

?第二种方式有什么好处呢?
?1,将资源对多线程共享。
?2,避免了单继承的局限性。
??
?-----------------
?线程的安全问题。
?造成的原因是什么?
?多条语句同时操作通一个成员变量,这些语句被多个线程分开执行会造成成员数据的错误。
?int num = 100;
?public void run()
?{
??while(true)
??{
???if(num>0)
????-->0? -->1
????System.out.println(num--);
??}
?}


?如何解决呢?
?可以通过同步的方式解决线程安全问题。
?原理:
?将出现安全问题的语句存放同步区域中,同步区域的好处在于给该区域加锁。
?这样就保证了某一时刻只有一个线程在同步区域内执行多条语句。

?弊端:消耗资源,对效率有降低。

?如果同步嵌套,容易出现死锁。

?在书写同步的时候前提:
?1,需要两个或者两个以上的线程。
?2,多个线程必须使用同一个锁。

?同通过一个比喻理解同步:火车上的卫生间。

?同步有两种表现形式:
?1,同步代码块:一个单独的封装区域。通过同步关键字定义的。锁是自定义的任意对象。
?2,同步函数:将同步关键字作为修饰符定义在函数上,让函数具备了同步的特性,使用的锁this。
??特例:静态同步函数使用的是什么锁呢?字节码文件所属对象。类名.class.

?单例设计模式的中的懒汉式设计模式的特点:
?class Single
?{
??private static? Single s = null;
??private Single(){}
??public static Single getInstance()
??{
???if(s == null)
????
????s = new Single();
???return s;
??}
?}
?这种懒汉式当多线程并发访问该方法时,会出现线程安全问题。

?if(s == null)
??-->0? -->1??
??s = new Single();
?为了解决这个问题,将这个方法修饰为同步的。
?public static synchronized Single getInstance()
?{
??if(s == null)???
???s = new Single();
??return s;
?}

?这是修饰虽然解决了安全问题,但是效率较低。因为每一个线程访问该方法时,都要判断Single.class这个锁。

?优化一下。通过双重判断的形式。
?public static? Single getInstance()
?{
??if(s==null)
??{
???--.0 --.1
???synchronized(Single.class)
???{
????if(s == null)???
?????s = new Single();
???}
??}
??return s;
?}
??
?---------
?线程间的通信。
?多个线程在操作同一个资源。但是操作的动作不一致。
?需要对这个动作分别进行描述,并存放在不同的run方法。

?通常可以可以将资源作为参数传递给run方法做在类的构造函数。

?wait(),notify(),notifyAll().也可以通过比喻:儿时的抓人游戏。
?
?notify():唤醒的是线程池中的第一个。

?这些方法都使用在同步中。
?用于操作线程。

?这些方法为什么定义在了Object类中?
?因为这三个方法,需要标识线程所属的锁。lock.wait(),lock.notify();
?而锁可以是任意对象,那么任意对象可以调用的方法定义在Object类中。

?wait(),sleep()的特点:
?wait():释放资源,释放锁。
?sleep():释放资源,不释放锁。

?-------------------------

?线程的停止:
?因为stop方法过时了。
?所以只有一方式:run方法结束。
?run方法中通常都会定义循环。
?1,定义结束标记。只要控制住循环,就可以结束run方法。
?但是这种情况也有局限:当线程处于了冻结状态,是不会执行标记的。
?2,interrupt():强制将冻结状态清除,让线程从冻结状态转回运行状态,就有机会执行到标记。
???但这种方式会发生异常。InterruptedException.
???可以作用于被wait,sleep,join、方法冻结的线程。
?
?
?-------------------------
?setPriority(int):设置线程的优先级。级别:1~10.
???有三个常量可用。MIN_PRIORITY=1,MAX_PRIORITY=10,NORM_PRIORITY=5
???所有线程默认是都是5。
?
?setDaemon(boolean):将线程标记成守护线程。
????有什么特点呢?当前台线程都结束后,守护线程自动结束。
????注意:必须要用的线程开启前。
?
?join():加入线程。当某一个线程A在执行时,执行到了B.join().那么A会释放出资源,
???等待B线程运行结束,A在恢复到运行状态。

?static void yield():让线程临时暂停,时间较短,只为延缓线程的速度。


?聊天程序使用了多线程技术。

?web服务器也使用了多线程技术。
?将每一个访问web服务端的客户端封装成一个线程。可以让多个客户端并发访问服务器。
??
?对于已有的服务端软件,如Tomcat。内部就封装了多线程技术。

---------------------------------------------

读书人网 >编程

热点推荐