您的位置:首页 > 职场人生

黑马程序员——学习笔记——多线程

2014-12-12 19:50 483 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流!
-------

时间:2014年12月12日

天气:多云转晴

心情:不好,因为今天花了500大洋,接下来没钱吃饭了T_T

好吧,总算复习了前面两章的东西了,昨天开始看多线程,两天时间,掌握的还行,那就把今天的笔记给弄上来

多线程

进程:正在进行中的程序(直译),程序运行时,内存开辟的一片空间。

线程:进程中一个负责程序执行的控制单元(执行路径),一个进程中可以有多个执行路径(多线程)。

注意:一个进程中至少要有一个线程,开启多个线程是为了同时运行多部分代码,每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。

多线程的好处:解决了多部分代码同时运行的问题。

弊端:线程太多,会导致效率的降低。

其实,多个应用程序同时执行都是CPU在做着快速的切换完成的。这个切换是随机的。CPU的切换是需要花费时间的,从而导致了效率的降低。

创建线程的目的就是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行,而运行的指定代码就是这个执行路径的任务。

jvm创建的主线程的任务都定义在了主函数中。

创建线程方式一:继承Thread类

class Demo extends Thread{

private String name ;
Demo(String name){
this.name = name;
}
public void run(){
for(int x = 0; x < 10; x++){
System. out.println(name + "...x=" + x + "...ThreadName=" + Thread.currentThread ().getName());
}
}
}

class ThreadDemo2{
public static void main(String[] args){
Demo d1 = new Demo("旺财" );
Demo d2 = new Demo("xiaoqiang" );
d1.start(); //开启线程,调用run方法。
d2.start();
for(int x = 0; x < 20; x++){
System. out.println("x = " + x + "...over..." + Thread.currentThread().getName());
}
}
}

创建线程方式二:实现Runnable接口

1、定义类实现Runnable接口。

2、覆盖接口中的run方法,将线程的任务代码封装到run方法中。

3、通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。

4、调用线程对象的start方法开启线程。

实现Runnable接口的好处:

1、将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。

2,避免了Java单继承的局限性。所以,创建线程的第二种方式较为常用。

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

class ThreadDemo3{
public static void main(String[] args){
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
}
}
       private int sum ;
       public void add(int num){
             synchronized(this ){
                   sum = sum + num;
                  System. out.println("sum = " + sum);
            }
      }
}

class Cus implements Runnable{
       private Bank b = new Bank();
       public void run(){
             for(int x = 0; x < 3; x++){
                   b.add(100);
            }
      }
}


同步函数和同步代码块的区别:1、同步函数的锁是固定的this。

2、同步代码块的锁是任意的对象。

建议使用同步代码块

class Ticket implements Runnable{
       private int num = 100;
       boolean flag = true;

       public void run(){
             if(flag ){
                   while(true ){
                         synchronized(this ){
                               if(num > 0){
                                     try{
                                          Thread. sleep(10);
                                    } catch(InterruptedException e){
                                          e.printStackTrace();
                                    }
                                    System. out.println(Thread.currentThread().getName() + "...obj..." + num--);
                              }
                        }
                  }
            } else
                   while(true )
                        show();
      }

       public synchronized void show(){
             if(num > 0){
                   try{
                        Thread. sleep(10);
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
                  System. out.println(Thread.currentThread().getName() + "...function..." + num--);
            }
      }
}

class SynFunctionLockDemo{
       public static void main(String[] args){
            Ticket t = new Ticket();
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            
            t1.start();
             try{
                  Thread. sleep(10);
            } catch(InterruptedException e){
                  e.printStackTrace();
            }
            t. flag = false ;
            t2.start();
      }
}


静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class表示
class Ticket implements Runnable{
       private static int num = 100;
      Object obj = new Object();
       boolean flag = true;

       public void run(){
             if(flag ){
                   while(true ){
                         synchronized(Ticket.class){//this.getClass()
                               if(num > 0){
                                     try{
                                          Thread. sleep(10);
                                    } catch(InterruptedException e){
                                          e.printStackTrace();
                                    }
                                    System. out.println(Thread.currentThread().getName() + "...obj..." + num--);
                              }
                        }
                  }
            } else
                   while(true )
                         show();
      }

      public static synchronized void show(){
             if(num > 0){
                   try{
                        Thread. sleep(10);
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
                  System. out.println(Thread.currentThread().getName() + "...function..." + num--);
            }
      }
}

class SynFunctionLockDemo{
       public static void main(String[] args){
            Ticket t = new Ticket();
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            
            t1.start();
             try{
                  Thread. sleep(10);
            } catch(InterruptedException e){
                  e.printStackTrace();
            }
            t. flag = false ;
            t2.start();
      }
}


多线程下的单例问题
饿汉式:
class Single{
       private static final Single s = new Single();
       private Single(){}
       public static Single getInstance(){
             return s ;
      }
}


饿汉式不存在安全问题,因为不存在多个线程共同操作数据的情况。
懒汉式:
class Single{
      private static Single s = null;
       private Single(){} 
       public static Single getInstance(){
             if(s ==null){
                   synchronized(Single.class){
                         if(s == null)
                               s = new Single();
                  }
            }
            return s ;
      }
}


懒汉式存在安全问题,可以使用同步函数解决。
但若直接使用同步函数,则效率较低,因为每次都需要判断。
public static synchronized Single getInstance(){
If(s==null){
S == new Single()
}
}


但若采取如下方式,即可提升效率。
public static Single getInstance(){
             if(s ==null){
                   synchronized(Single.class){
                         if(s == null)
                               s = new Single();
                  }
            }
            return s ;


原因在于任何一个线程在执行到第一个if判断语句时,如果Single对象已经创建,则直接获取即可,而不用判断是否能够获取锁,相对于上面使用同步函数的方法就提升了效率。
如果当前线程发现Single对象尚未创建,则再判断是否能够获取锁。

死锁
死锁常见情景之一:同步的嵌套。
class Ticket implements Runnable{
       private static int num = 100;
      Object obj = new Object();
       boolean flag = true;

       public void run(){
             if(flag ){
                   while(true ){
                         synchronized(obj ){
                              show();
                        }
                  }
            } else
                   while(true )
                        show();
      }

       public synchronized void show(){
             synchronized(obj ){
                   if(num > 0){
                         try{
                              Thread. sleep(10);
                        } catch(InterruptedException e){
                              e.printStackTrace();
                        }
                        System. out.println(Thread.currentThread().getName() + "...function..." + num--);
                  }
            }
      }
}

class DeadLockDemo{
       public static void main(String[] args){
            Ticket t = new Ticket();
            Thread t1 = new Thread(t);
            Thread t2 = new Thread(t);
            
            t1.start();
             try{
                  Thread. sleep(10);
            } catch(InterruptedException e){
                  e.printStackTrace();
            }
            t. flag = false ;
            t2.start();
      }
} 
结果:



例2

class Test implements Runnable{
       private boolean flag ;
      Test( boolean flag){
             this.flag = flag;
      }

       public void run(){
             if(flag ){
                   while(true )
                         synchronized(MyLock.locka){
                              System. out.println(Thread.currentThread().getName() + "...if locka...");
                               synchronized(MyLock.lockb){
                                    System. out.println(Thread.currentThread().getName() + "...if lockb...");
                              }
                        }
            } else{
                   while(true )
                         synchronized(MyLock.lockb){
                              System. out.println(Thread.currentThread().getName() + "...else lockb...");
                               synchronized(MyLock.locka){
                                    System. out.println(Thread.currentThread().getName() + "...else locka...");
                        }
                  }
            }
      }
}

class MyLock{
       public static final Object locka = new Object();
       public static final Object lockb = new Object();
}

class DeadLockTest{
       public static void main(String[] args){
            Test a = new Test(true );
            Test b = new Test(false );

            Thread t1 = new Thread(a);
            Thread t2 = new Thread(b);

            t1.start();
            t2.start();
      }
}
结果:



线程间通信
线程间通信涉及的方法
多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。

等待/唤醒机制涉及的方法:

① wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。

② notify():唤醒线程池中的一个线程(任何一个都有可能)。

③ notifyAll():唤醒线程池中的所有线程。

注意:
1、这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。

2、必须要明确到底操作的是哪个锁上的线程!

3、wait和sleep区别?

①wait可以指定时间也可以不指定。sleep必须指定时间。

②在同步中时,对CPU的执行权和锁的处理不同。

wait:释放执行权,释放锁。

sleep:释放执行权,不释放锁。

生产者-消费者问题:
class Resource{
       private String name ;
       private String sex ;
       private boolean flag = false;

       public synchronized void set(String name,String sex){
             if(flag )
                   try{
                         this.wait();
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
             this.name = name;
             this.sex = sex;
             flag = true ;
             this.notify();
      }
      
       public synchronized void out(){
             if(!flag )
                   try{
                         this.wait();
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
            System. out.println(name + "..." + sex);
             flag = false ;
             this.notify();
      }
      
}

//输入
class Input implements Runnable{
      Resource r;
      Input(Resource r){
             this.r = r;
      }

       public void run(){
             int x = 0;
             while(true ){
                   if(x == 0){
                         r.set( "mike","男" );
                  } else{
                         r.set( "lili","女" );
                  }
                  x = (x + 1)%2;
            }
      }
}

//输出
class Output implements Runnable{
      Resource r;

      Output(Resource r){
             this.r = r;
      }

       public void run(){
             while(true ){
                   r.out();
            }
      }
}

class ResourceDemo {
       public static void main(String[] args){
             //创建资源
            Resource r = new Resource();
             //创建任务
            Input in = new Input(r);
            Output out = new Output(r);
             //创建线程,执行路径
            Thread t1 = new Thread(in);
            Thread t2 = new Thread(out);
             //开启线程
            t1.start();
            t2.start();
      }
结果:



多生产者多消费者问题:
class Resource{
       private String name ;
       private int count = 1;
       private boolean flag = false;

       public synchronized void set(String name){
             if(flag )
                   try{
                        wait();
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
             this.name = name + count;
             count++;
            System. out.println(Thread.currentThread().getName() + "...生产者..." + this. name);
             flag = true ;
            notify();
      }

       public synchronized void out(){
            if(!flag )
                   try{
                        wait();
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
            flag = false ;
            notify();
            System. out.println(Thread.currentThread().getName() + "...消费者..." + this. name);
      }
}

class Producer implements Runnable{
      private Resource r ;
      Producer(Resource r){
             this.r = r;
      }
       public void run(){
             while(true ){
                   r.set( "烤鸭");
            }
      }
}

class Consumer implements Runnable{
      private Resource r ;
      Consumer(Resource r){
             this.r = r;
      }
       public void run(){
             while(true ){
                   r.out();
            }
      }
}
 
class ProducerConsumerDemo {
       public static void main(String[] args){
            Resource r = new Resource();
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);

            Thread t0 = new Thread(pro);
            Thread t1 = new Thread(pro);
            Thread t2 = new Thread(con);
            Thread t3 = new Thread(con);
            t0.start();
            t1.start();
            t2.start();
            t3.start();
      }
} 


由于if判断标记,只有一次,会导致不该运行的线程运行了,出现了数据错误的情况。故修改成while判断标记,线程获取CPU执行权及锁后,将重新判断是否具备运行条件。
notify方法只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。notifyAll解决了本方线程一定会唤醒对方线程的问题。

class Resource{
       private String name ;
       private int count = 1;
       private boolean flag = false;

       public synchronized void set(String name){
            while(flag )
                   try{
                         this.wait();
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
             this.name = name + count;
             count++;
            System. out.println(Thread.currentThread().getName() + "...生产者..." + this. name);
             flag = true ;
            notifyAll();
      }

       public synchronized void out(){
             while(!flag )
                   try{
                         this.wait();
                  } catch(InterruptedException e){
                        e.printStackTrace();
                  }
             flag = false ;
            notifyAll();
            System. out.println(Thread.currentThread().getName() + "...消费者..." + this. name);
      }
}

class Producer implements Runnable{
       private Resource r ;
      Producer(Resource r){
             this.r = r;
      }
       public void run(){
             while(true ){
                   r.set( "烤鸭");
            }
      }
}

class Consumer implements Runnable{
       private Resource r ;
      Consumer(Resource r){
             this.r = r;
      }
       public void run(){
             while(true ){
                   r.out();
            }
      }
}

class ProducerConsumerDemo {
       public static void main(String[] args){
            Resource r = new Resource();
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);

            Thread t0 = new Thread(pro);
            Thread t1 = new Thread(pro);
            Thread t2 = new Thread(con);
            Thread t3 = new Thread(con);
            t0.start();
            t1.start();
            t2.start();
            t3.start();
      }
}
结果:



JDK1.5新特性-Lock、Condition
同步代码块就是对于锁的操作是隐式的。

JDK1.5以后将同步和锁封装成了对象,并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。

Lock接口:出现替代了同步代码块或者同步函数,将同步的隐式操作变成显示锁操作。同时更为灵活,可以一个锁上加上多组监视器。

lock():获取锁。

unlock():释放锁,为了防止异常出现,导致锁无法被关闭,所以锁的关闭动作要放在finally中。

Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。

Condition接口中的await方法对应于Object中的wait方法。

Condition接口中的signal方法对应于Object中的notify方法。

Condition接口中的signalAll方法对应于Object中的notifyAll方法。

<pre name="code" class="java">class Resource{
       private String name ;
       private int count = 1;
       private boolean flag = false;
      
      //创建一个锁对象
      Lock lock = new ReentrantLock();

       //通过已有的锁获取该锁上的监视器对象      
      Condition con = lock .newCondition();

       public void set(String name){
             lock.lock();
             try{
                   while(flag )
                         try{
                              con.await();
                        } catch(InterruptedException e){
                              e.printStackTrace();
                        }
                   this.name = name + count;
                   count++;
                  System. out.println(Thread.currentThread().getName() + "...生产者..." + this. name);
                   flag = true ;
                   con.signalAll();
            }finally{
                   lock.unlock();
            }
      }

       public void out(){
            lock.lock();
             try{
                   while(!flag )
                         try{
                              con.await();
                        } catch(InterruptedException e){
                              e.printStackTrace();
                        }
                   flag = false ;
                   con.signalAll();
                  System. out.println(Thread.currentThread().getName() + "...消费者..." + this. name);
            }finally{
                   lock.unlock();
            }
      }
}

class Producer implements Runnable{
       private Resource r ;
      Producer(Resource r){
             this.r = r;
      }
       public void run(){
             while(true ){
                   r.set( "烤鸭");
            }
      }
}

class Consumer implements Runnable{
       private Resource r ;
      Consumer(Resource r){
             this.r = r;
      }
       public void run(){
             while(true ){
                   r.out();
            }
      }
}

class ProducerConsumerDemo {
       public static void main(String[] args){
            Resource r = new Resource();
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);

            Thread t0 = new Thread(pro);
            Thread t1 = new Thread(pro);
            Thread t2 = new Thread(con);
            Thread t3 = new Thread(con);
            t0.start();
            t1.start();
            t2.start();
            t3.start();
      }
}
结果:




使用一个Lock、两个Condition修改上面的多生产者-多消费者问题:

class Resource{
       private String name ;
       private int count = 1;
       private boolean flag = false;
      
       //创建一个锁对象
      Lock lock = new ReentrantLock();

       //通过已有的锁获取该锁上的监视器对象      
      Condition con = lock .newCondition();

      //通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者
      Condition producer_con = lock .newCondition();
      Condition consumer_con = lock .newCondition();

       public void set(String name){
             lock.lock();
             try{
                   while(flag )
                         try{
                              producer_con.await();
                        } catch(InterruptedException e){
                              e.printStackTrace();
                        }
                   this.name = name + count;
                   count++;
                  System. out.println(Thread.currentThread().getName() + "...生产者..." + this. name);
                   flag = true ;
                   consumer_con.signal();
            } finally{
                   lock.unlock();
            }
      }

       public void out(){
             lock.lock();
             try{
                   while(!flag )
                         try{
                               consumer_con.await();
                        } catch(InterruptedException e){
                              e.printStackTrace();
                        }
                   flag = false ;
                   producer_con.signal();
                  System. out.println(Thread.currentThread().getName() + "...消费者..." + this. name);
            } finally{
                   lock.unlock();
            }
      }
}

class Producer implements Runnable{
       private Resource r ;
      Producer(Resource r){
             this.r = r;
      }
       public void run(){
             while(true ){
                   r.set( "烤鸭");
            }
      }
}

class Consumer implements Runnable{
       private Resource r ;
      Consumer(Resource r){
             this.r = r;
      }
       public void run(){
             while(true ){
                   r.out();
            }
      }
}

class ProducerConsumerDemo {
       public static void main(String[] args){
            Resource r = new Resource();
            Producer pro = new Producer(r);
            Consumer con = new Consumer(r);

            Thread t0 = new Thread(pro);
            Thread t1 = new Thread(pro);
            Thread t2 = new Thread(con);
            Thread t3 = new Thread(con);
            t0.start();
            t1.start();
            t2.start();
            t3.start();
      }
}


停止线程
任务中都会有循环结构,只要控制住循环就可以结束任务。

控制循环通常就用定义标记来完成。

class StopThread implements Runnable{
       private boolean flag = true;
       public void run(){
             while(flag ){
                  System. out.println(Thread.currentThread().getName() + "...");
            }
      }
       public void setFlag(){
             flag = false ;
      }
}
 
class StopThreadDemo{
       public static void main(String[] args){
            StopThread st = new StopThread();

            Thread t1 = new Thread(st);
            Thread t2 = new Thread(st);

            t1.start();
            t2.start();

             int num = 1;
             for(;;){
                   if(++num == 50){
                        st.setFlag();
                         break;
                  }
                  System. out.println("main..." + num);
            }
            System. out.println("over" );
      }
}

也可以使用stop方法停止线程,不过已经过时,不再使用。

但是如果线程处于了冻结状态,无法读取标记,如何结束呢?

可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格。强制动作会发生InterruptedException,一定要记得处理。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: