黑马程序员_多线程及单例模式

来源:互联网 发布:路由器对网络稳定性 编辑:程序博客网 时间:2024/05/20 20:48

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------

进程与线程:

 

进程,正在运行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径

 

,或者叫一个控制单元。

 

线程,就是进程中的一个独立的控制单元。线程在控制着进程的执行。一个进程中至少有一个线程。

 

其实更细节说明jvm启动不止一个线程,还有负责垃圾回收机制的线程。

 

线程都有自己的默认名称,也可以通过setName()方法,或者通过构造方法来给线程设置自定义的名称。

 

 

启动线程的两种方式:

 

第一种方式:

 

继承Thread类:


1、定义类继承Thread。


2、复写Thread类中的run方法。目的:将自定义代码存储在run方法。让线程运行。


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


Thread类用于描述线程。


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

 

也就是说Thread类中的run方法,用于存储线程要运行的代码。

 

Java代码  收藏代码
  1. class Demo extends Thread {  
  2.   
  3.     @Override  
  4.     public void run() { //重写Thread的run()方法  
  5.           
  6.         System.out.println("demo run");  
  7.     }  
  8.       
  9.       
  10. }  
  11.   
  12.     public static void main(String[] args) throws IOException {  
  13.   
  14.         Demo demo = new Demo();  
  15.           
  16.         //开启线程的同时并执行该线程下的run方法,run方法并不能启动一个新的线程。  
  17.         demo.start();  
  18.     }  

 

 

 

 

第二种方式:


实现Runnable接口:


1、定义类实现Runnable接口


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

 

3、通过Thread类建立线程对象。


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


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


 因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去执行对象的run方

 

法。就必须明确该run方法所属对象。


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

 

 

Java代码  收藏代码
  1. public class Demo {  
  2.   
  3.     public static void main(String[] args) throws IOException {  
  4.   
  5.         Demo d = new Demo();  
  6.           
  7.         Thread t1 = new Thread(d);//创建线程,并将Runnable接口的子类对象传递给Thread  
  8.           
  9.         t1.start();//启动线程  
  10.           
  11.     }  
  12.   
  13. }  
  14.   
  15. class Demo implements Runnable {  
  16.   
  17.     @Override  
  18.     public void run() {//实现接口的run方法  
  19.           
  20.         System.out.println("demo run");  
  21.     }  
  22. }  

 

 

两种方式区别:

 

1、实现方式的好处:避免了单继承的局限性;在定义线程时,建议使用实现方式。

 

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

 

3、实现方式的线程代码存放在接口的子类的run方法中。

 

 

线程状态分析图:


 

 

多线程的安全问题:

 

导致安全问题的出现的原因:

 

 

1、多个线程访问出现延迟。

 

 

 

2、线程随机性。

 

 

同步(synchronized):

 

1、同步代码块:

 

synchronized(对象)

{

         需要同步的代码;

}

 

2、同步函数

 

         1、同步函数的锁是this。

 

         2、如果同步函数被静态修饰,则锁为Class。

 

同步的前提:

 

1、同步需要两个或者两个以上的线程。

 

2、多个线程使用的是同一个锁。

 

未满足这两个条件,不能称其为同步。

 

同步的弊端:

 

当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的

 

运行效率。

 

 

Java代码  收藏代码
  1. class Ticket implements Runnable  
  2. {  
  3.     private  int tick = 1000;  
  4.     Object obj = new Object();//锁对象,锁可以是任何对象,任何对象又都继承于Object  
  5.     public void run()  
  6.     {  
  7.         while(true)  
  8.         {  
  9.             synchronized(obj)//同步代码块  
  10.             {  
  11.                 if(tick>0)  
  12.                 {  
  13.                     System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);  
  14.                 }  
  15.             }  
  16.         }  
  17.     }  
  18. }  

 

 

同步中出现的死锁现象:

 

两把锁嵌套使用则有可能出现死锁。

 

Java代码  收藏代码
  1. class Test implements Runnable  
  2. {  
  3.     private boolean flag;//标志位  
  4.       
  5.     Test(boolean flag)//构造方法中传入一个标志初始化值  
  6.     {  
  7.         this.flag = flag;  
  8.     }  
  9.   
  10.     public void run()  
  11.     {  
  12.         if(flag)//如果flag为true,则进入同步代码块,拿到锁locka,若此时另一个线程已经开始运行,并flag的值为false,则拿到了锁lockb  
  13.         {           // 当拿有locka锁的线程要进入b锁的代码块时,lockb却被另一个线程持有,而此时持有lockb锁的线程也在等待locka锁,这样发生嵌套的情况,  
  14.                     //即会发生死锁情况。  
  15.             while(true)  
  16.             {  
  17.                 synchronized(MyLock.locka)  
  18.                 {  
  19.                     System.out.println(Thread.currentThread().getName()+"...if locka ");  
  20.                     synchronized(MyLock.lockb)  
  21.                     {  
  22.                         System.out.println(Thread.currentThread().getName()+"..if lockb");                    
  23.                     }  
  24.                 }  
  25.             }  
  26.         }  
  27.         else  
  28.         {  
  29.             while(true)  
  30.             {  
  31.                 synchronized(MyLock.lockb)  
  32.                 {  
  33.                     System.out.println(Thread.currentThread().getName()+"..else lockb");  
  34.                     synchronized(MyLock.locka)  
  35.                     {  
  36.                         System.out.println(Thread.currentThread().getName()+".....else locka");  
  37.                     }  
  38.                 }  
  39.             }  
  40.         }  
  41.     }  
  42. }  
  43.   
  44.   
  45. class MyLock  
  46. {  
  47.     static Object locka = new Object();//定义locka锁对象  
  48.     static Object lockb = new Object();//定义lockb锁对象  
  49. }  
  50.   
  51. class  DeadLockTest  
  52. {  
  53.     public static void main(String[] args)   
  54.     {  
  55.         //创建并启动两个线程  
  56.         Thread t1 = new Thread(new Test(true));  
  57.         Thread t2 = new Thread(new Test(false));  
  58.         t1.start();  
  59.         t2.start();  
  60.     }  
  61. }  

  

 

线程间通信:

 

就是多个线程在操作同一个资源,但是操作的动作不同。以下用生产者消费者的示例演示线程间通信方法,生产一个消费一个。

 

Java代码  收藏代码
  1. class IOtest1 {  
  2.       
  3.     public static void main(String[] args) throws InterruptedException {  
  4.           
  5.         /** 
  6.          * 创建4个线程同时执行,其中两个生产者和两个消费者 
  7.          * 但商品依然是生产一个消费一个的方式进行 
  8.          */  
  9.         Resource r = new Resource();  
  10.           
  11.         Producer pro = new Producer(r);  
  12.           
  13.         Consumer con = new Consumer(r);  
  14.           
  15.         Thread t1 = new Thread(pro);  
  16.           
  17.         Thread t2 = new Thread(con);  
  18.           
  19.         Thread t3 = new Thread(pro);  
  20.           
  21.         Thread t4 = new Thread(con);  
  22.           
  23.         //启动这4个线程  
  24.         t1.start();  
  25.         t2.start();  
  26.         t3.start();  
  27.         t4.start();  
  28.     }  
  29.   
  30. }  
  31.   
  32. class Resource {  
  33.       
  34.     private String name;  
  35.       
  36.     private int count = 1;  
  37.       
  38.     private boolean flag = false;  
  39.       
  40.     /** 
  41.      * 资源生产方法 
  42.      * @param name 
  43.      */  
  44.     public synchronized void set(String name) {  
  45.           
  46.         while(flag){ //if和while区别:当等待中的线程再次被唤醒时,if不需要再次判断标志位,直接向下执行,而while需要重新判断标志位  
  47.               
  48.             try {  
  49.                 this.wait();//线程等待  
  50.             } catch (InterruptedException e) {  
  51.                 e.printStackTrace();  
  52.             }  
  53.         }  
  54.         this.name = name + "..." + count++;  
  55.           
  56.         System.out.println(Thread.currentThread().getName()+"...生产者"+this.name);//打印生产  
  57.           
  58.         flag = true;  
  59.         this.notifyAll(); //notify和notifyAll方法:前者只能唤醒先进入线程池中的,并且持有该锁的线程,而后者可以唤醒线程池中所有持有该锁的线程  
  60.     }  
  61.       
  62.     /** 
  63.      * 资源消费方法 
  64.      */  
  65.     public synchronized void out() {  
  66.           
  67.         while(!flag) {  
  68.             try {  
  69.                 this.wait();  
  70.             } catch (InterruptedException e) {  
  71.                 e.printStackTrace();  
  72.             }  
  73.         }  
  74.         System.out.println(Thread.currentThread().getName()+"...消费者"+this.name);//打印消费  
  75.           
  76.         flag = false;  
  77.         this.notifyAll();  
  78.     }  
  79. }  
  80.   
  81. /** 
  82.  * 生产者类 
  83.  * @author Administrator 
  84.  * 
  85.  */  
  86. class Producer implements Runnable {  
  87.   
  88.     private Resource res;  
  89.       
  90.     Producer(Resource res) {  
  91.           
  92.         this.res = res;  
  93.     }  
  94.       
  95.     @Override  
  96.     public void run() {  
  97.   
  98.         while(true) {  
  99.               
  100.             res.set("-商品-");//调用生产方法  
  101.         }  
  102.     }  
  103.       
  104. }  
  105.   
  106. /** 
  107.  * 消费者类 
  108.  * @author Administrator 
  109.  * 
  110.  */  
  111. class Consumer implements Runnable {  
  112.       
  113.     private Resource res;  
  114.       
  115.     Consumer(Resource res) {  
  116.           
  117.         this.res = res;  
  118.     }  
  119.       
  120.     @Override  
  121.     public void run() {  
  122.   
  123.         while(true) {  
  124.               
  125.             res.out();//调用消费方法  
  126.         }  
  127.     }  
  128. }  

 

 

wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中:

 

1,这些方法存在与同步中。


2,使用这些方法时必须要标识所属的同步的锁。

 

3,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

 

 

wait(),sleep()有什么区别:

 

wait():释放cpu执行权,释放锁。

 

sleep():释放cpu执行权,不释放锁。

 

 

 


停止线程:


只有run方法结束后,线程便会停止。

 

开启多线程运行,运行代码通常是循环结构,只要控制好循环,就可以让线程停止。

 

当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。

 

强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。

 

Thread类提供该方法 interrupt();

 


守护线程:
当正在运行的线程都为守护线程时,jvm将退出。
setDaemon方法可以设置为守护线程。必须在启动线程之前执行。
 
Join方法
T1.join();当前线程释放执行权,并进入冻结状态,让T1执行。

 

1.5版本JDK中的线程锁:

 

Java代码  收藏代码
  1. <strong>private Lock lock = new ReentrantLock();  
  2.       
  3.     Condition con = lock.newCondition();  
  4. </strong>  

 

 

一个Lock中可以有多个Condition。

 

 

单例设计模式:

 

 

单例设计模式:解决一个类在内存只存在一个对象。

 

保证对象唯一:

 

1、为了避免其他程序过多建立该类对象。先禁止其他程序建立该类对象


2、还为了让其他程序可以访问到该类对象,只好在本类中,自定义一个对象。


3、为了方便其他程序对自定义对象的访问,可以对外提供一些访问方式。

 

代码体现过程:

 

1、将构造函数私有化。


2、在类中创建一个本类对象。


3、提供一个方法可以获取到该对象。

 

 

饿汉式:

 

Java代码  收藏代码
  1. private static Single s = new Single();  
  2.       
  3.     private Single() {}  
  4.       
  5.     public static Single getInstance() {  
  6.         return s;  
  7.     }  

 

 

 

懒汉式(解决懒汉式线程安全问题):

 

 
Java代码  收藏代码
  1. class Single {  
  2.       
  3.     private static Single s = null;//延迟加载  
  4.       
  5.     private Single() {}  
  6.       
  7.     public static Single getInstance() {  
  8.           
  9.         if(s == null) {//为了提高运行效率,在判断锁之前再判断一次对象是否存在  
  10.             synchronized(Single.class) {  
  11.                 if(s == null) {  
  12.                     s = new Single();  
  13.                 }  
  14.             }  
  15.         }  
  16.         return s;  
  17.     }  
  18. }  

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity开发</a>、<a href="http://www.itheima.com"target="blank">.Net培训</a>、期待与您交流! ----------------------

0 0