java 多线程详解

来源:互联网 发布:ubuntu添加vga 788 编辑:程序博客网 时间:2024/06/03 17:07

关于线程的概念

1、程序一般是指一段静态代码,静态对象;进程是程序的一次执行过程或正在运行的程序动态的的过程,有它的产生、存在和消亡的过程;线程是指一个程序内部的一条执行路径,也就是说每一个任务就代表一个线程。进程与线程相互联系然而又有区别,从本质上来说每个进程都有自己的一整套变量,而线程间可以共享变量,共享变量使得线程之间的通信比进行之间通信更有效、更容易。
2、并不是每种情况都需要多线程,当程序需要同时执行两个或多个任务;当程序需要实现一些等待任务,如用户的输入,文件读写,网络操作,搜索;当程序需要运行一些后台程序。在这些情况下我们需要使用多线程。
3、线程的生命周期:每个线程都有新建、就绪、运行、阻塞、死亡这五种状态


创建线程的两种方式

1、继承Thread类:
a.创建一个类继承Thread;b.重写run()方法,将要执行的业务逻辑代码放在run()方法体中;c.在主类中创建子线程类的实例;d.调用线程方法start(),将此线程变为就绪状态。
package com.thread;public class TestThread {public static void main(String [] args){       //3、创建一个子类对象SubThread st=new SubThread();//4、调用线程的start()方法,开始启动线程;调用相应的run()方法st.start();//这个是主线程的方法 for(int i=1;i<=100;i++){System.out.println(Thread.currentThread().getName()+":"+i);}}}//1、创建一个继承Thread的子类class SubThread extends Thread{//2、重写run()方法@Overridepublic void run() {            for(int i=1;i<=100;i=i+2){ System.out.println(Thread.currentThread().getName()+":"+i);    }}}
2、实现Runnable接口
a.创建一个类实现Runnable接口;b.重写run()方法,将业务逻辑代码放入在run()方法中;c.创建一个Runbable接口实现类的对象的实例 d.将这个对象作为形参传递给Thread类的构造器中,创建Thread类对象,然后再启动线程start()方法。
package com.thread;public class ThreadTest {    public static void main(String[] args) throws Exception{    MyRunnable r=new MyRunnable();//c.创建一个Runbable接口实现类的对象的实例     Thread t=new Thread(r);//d.将这个对象作为形参传递给Thread类的构造器中,创建Thread类对象,然后再启动线程start()方法。    t.setName("自定义线程:");    t.start();    System.out.println(Thread.currentThread().getName());}}class MyRunnable implements Runnable{//a.创建一个类实现Runnable接口@Overridepublic void run() {//b.重写run()方法,将业务逻辑代码放入在run()方法中           for(int i=0;i<10;i++){                  System.out.println(Thread.currentThread().getName()+i);       }}}

3、这两种方式创建线程的异同在于a.Thread类其实也是实现Runnable接口;b.实现Runnable接口的方式要优于继承Thread类,首先实现的方式避免java中单继承的缺点,其次如果多个线程操作同一份数据资源,更加适合用实现的方式

线程中Thread类常用的方法

1、start():启动线程并执行相应的run方法
2、Thread.currentThread()   Thread.currentThread().getName()、Thread.currentThread().setName("设置的name")           获取当前线程/获取当前线程的名字/设置当前线程的名字
3、sleep(long time):显式的让线程休眠,其中time以毫秒为单位
4、yield():让调用这个方法的线程释放当前cpu的执行权利
5、线程通信的方法:wait():另当前线程挂起并放弃cpu、同步资源使别的线程可以访问并修改共享资源,而当前线程排队等待再一前次对资源的访问;
                                   notify():唤醒正在排队等待同步资源的线程中优先级别最高者结束等待;
                                   notifyAll():唤醒所有正在排队等待资源的线程结束等待。

6、join():在A线程中调用B线程的Join()方法,表示强制执行B线程,这个时候A线程停止执行,直到B执行完毕A才可以继续执行。

线程的安全问题

一、线程安全问题的原因:由于一个线程正在操作共享数据,未执行完毕的情况下,另外一个线程参与进来,导致共享数据在安全上出了问题。如银行的转账存款业务,卖票业务等等。
二、解决线程安全的办法 
1、同步块synchronized:将需要同步的业务逻辑代码放在synchronized(this){ ...}这个同步块中
package com.thread;public class Demo {public static void main(String[] args) {sell_Window sell=new sell_Window();Thread t1=new Thread(sell);//开启三个窗口同时卖票Thread t2=new Thread(sell);Thread t3=new Thread(sell);t1.start();t2.start();t3.start();}}class sell_Window implements Runnable{//实现Runnable接口,把买票的业务逻辑放在run()方法中    int num=100;     @Override    public void run(){      while(true){ synchronized(this) {//利用同步块来实现线程安全,this表示当前类,我们也可以使用一个对象来做锁对象,其中利用</span><span style="font-size:12px;">Demo.class字节码也是比较好的     if(num>0){                   try{Thread.sleep(100);                     System.out.println("窗口 "+Thread.currentThread().getName()+":售第"+(num--)+"张票");      }catch (Exception e) {e.printStackTrace();      }      }else{ break;    }        }     }   }}

2、同步方法synchronized:将需要同步的方法加上关键字:synchronized如publicsynchronized void set(String name){...}
package com.thread;public class Demo {public static void main(String[] args) {sell_Window sell=new sell_Window(new SellTicket());Thread t1=new Thread(sell);//开启三个窗口同时卖票Thread t2=new Thread(sell);Thread t3=new Thread(sell);t1.start();t2.start();t3.start();}}class SellTicket{//共享数据区,以及买票的方法int num=100;public synchronized void sell(){//同步方法if(num>0){System.out.println("窗口 "+Thread.currentThread().getName()+":售第"+(num--)+"张票");}else{Thread.currentThread().stop();}}}class sell_Window implements Runnable{//实现Runnable接口,把买票的业务逻辑放在run()方法中SellTicket sellTicket;public sell_Window(SellTicket sellTicket) {this.sellTicket=sellTicket;}@Overridepublic void run(){  while(true){  try{  Thread.sleep(100);  sellTicket.sell();  }catch (Exception e) {e.printStackTrace();}   }}}
3、锁Lock,在线程同步中的Lock其实功能上和synchronized有着异曲同工的效果,不过Lock可以提供更加细度的控制,在java中使用Lock锁的方式如下:
package com.thread;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/*线程同步中的锁Lock */public class ThreadLock {public static void main(String[] args) {     new ThreadLock().init();}public void init(){   final Outputer outputer=new Outputer();   new Thread(new Runnable() {//匿名线程         @Overridepublic void run() {//run()方法     while(true){   try {       Thread.sleep(10);                              outputer.output("zhangxiaoxiang");//执行的任务                } catch (InterruptedException e) {                             e.printStackTrace();                         }               }             }          }).start();            new Thread(new Runnable() {                  @Override                 public void run() {                      while(true){                           try {                                  Thread.sleep(10);                                        outputer.output("admin");                                } catch (InterruptedException e) {                                       e.printStackTrace();                               }                       }                 }           }).start();        }   static class Outputer{          Lock lock=new ReentrantLock();//获取锁          public void output(String name) {              int len=name.length();              lock.lock();//加锁              try{                  for(int i=0;i<len;i++){                    System.out.print(name.charAt(i));                  }                  System.out.println();              }catch(Exception e){                  e.printStackTrace();              }finally{                     lock.unlock();//释放锁              }            }       }   }
在Lock中存在比较有用的两种锁,读写锁(ReadWriteLock),使用这样的锁可以保证读文件的时候可以并发,读写和写文件不可以并发。下面用一个比较实用的例子来说明读写锁的优点。
设计一个简易的cache系统:
package com.thread;import java.util.HashMap;import java.util.Map;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;/*设置缓存系统: * 多个人进行读取数据时,可以并行,所以这时候只需要读锁;当有人发现没有数据时,这时候需释放掉 * 读锁加上一个写锁,在写的时候不能被读取,写完之后就还原成读锁让其它人可以读取 *  */public class Thread_CacheDemo {private Map<String,Object> cache=new HashMap<String,Object>();public static void main(String[] args) {}//得到数据private ReadWriteLock rwl=new ReentrantReadWriteLock();public Object getData(String key){//多线程读取时rwl.readLock().lock();//读锁Object value=null;try{value=cache.get(key);if(value == null){rwl.readLock().unlock();//释放读锁rwl.writeLock().lock();//写锁try {    if(value == null){    value="aaaa";//这里实际上是查询数据库    }} catch (Exception e) {}finally{rwl.writeLock().unlock();//释放写锁rwl.readLock().lock();//加上读锁}}}catch (Exception e) {e.printStackTrace();}finally{rwl.readLock().unlock();//释放读锁}    return value;}}

线程间通信

1、线程通信的概念:线程通信是指线程间相互交流的过程,一个线程可以释放自己的cpu权利来给其它被唤醒的线程使用。

2、线程通信的方法,线程通信主要用到了wait(),notif(),notifyAll(),线程通信的一个经典案例就是生产者和消费者调度算法。下面是生产者和消费者调度算法的实现过程:

package com.thread;/*生产者与消费者的问题 *     生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品 *   ,店员一次只能持有固定数量的产品(比如20),如果生产者试图生 产更多的产品,店员会叫生产者停一下, *   如果店中有空位放产品了再通知生产者继续生产;如果店中没产品了,店员会告诉消费者等一下,如果店中有了产品再 *   通知消费者。 *    1、多线程:生产者,消费者 *    2、共享数据(资源):产品的数量  *    3、需要线程安全 *    4、需要线程通信:生产者与消费者通信 * */public class Produce_consume {public static void main(String[] args) {Clerk clerk=new Clerk();//共享数据Producer p=new Producer(clerk);//生产者Consumer c=new Consumer(clerk);//消费者Thread t1=new Thread(p);//生产者线程Thread t3=new Thread(p);//生产者线程Thread t2=new Thread(c);//消费者线程t1.setName("生产者1");t2.setName("消费者");t3.setName("生产者2");t1.start();t2.start();t3.start();}}//生产者class Producer implements Runnable{Clerk clerk;public Producer(Clerk clerk) {this.clerk=clerk;}@Overridepublic void run() {System.out.println("生产者开始生产产品");while(true){  try{Thread.currentThread().sleep(10);                       clerk.addProduct();//生产者生产产品                  } catch (InterruptedException e) {                       e.printStackTrace();                   }               }             }           }//消费者class Consumer implements Runnable{    Clerk clerk;    public Consumer(Clerk clerk){         this.clerk=clerk;    }    @Override    public void run(){        System.out.println("消费者消费产品");         while(true){             try {                     Thread.currentThread().sleep(10);                   clerk.consumeProduct(); //消费者消费产品            } catch(InterruptedException e) {                   e.printStackTrace();            }         }   }}//店员,这是一个共享的数据,供生产者和消费者使用class Clerk{    int product;    public synchronized void addProduct(){             //生产产品            if(product>=20){              try{               //产品数大于或等于20,暂停生产               wait();//释放cpu资源               } catch (InterruptedException e){                e.printStackTrace();              }            }else{                product++;                System.out.println(Thread.currentThread().getName()+":生产了第"+product+"个产品");                notifyAll();//一旦有产品生产,去唤醒消费者进行消费,notifyAll()唤醒其它线程            }           }   public synchronized void consumeProduct(){    //消费产品     if(product<=0){            try {               wait();//产品数少于或等于0,停止消费            } catch (InterruptedException e) {                e.printStackTrace();           }    }else{       System.out.println(Thread.currentThread().getName()+":消费了第"+product+"个产品");       product--;       notifyAll();//有消费,就去唤醒生产者进行生产    }   }}

线程范围的共享数据ThreadLocal类

1、ThreadLocal类表示可以存放当前线程的变量,同时这个变量可以让这个线程中的所有对象共享。这个类的底层实现方式和HashMap()大致相同,它分别有get(),set(),remove(),setInitialValue()四个方法,这四个方法分别表示得到当前线程相关的变量、设置当前线程的相关变量、把与当前线程有关的变量全部remve、设置初始化的值。当我们遇到需要在一个线程中放置多个变量的情况时,可以把这些变量封装成一个对象来存储。
package com.thread;import java.util.Random;public class ThreadLocal_fun {   private static ThreadLocal<Integer> x=new ThreadLocal<Integer>();//表示放置一个变量   private static ThreadLocal<MyThreadScopeDate> xObj=new ThreadLocal<MyThreadScopeDate>();//放置一个变量对象   public static void main(String[] args) {  for(int i=0;i<2;i++){//开启两个线程 new Thread(new Runnable() {    @Override    public void run() {                int data=new Random().nextInt();                System.out.println(Thread.currentThread().getName()+" has put data:"+data);                x.set(data);                MyThreadScopeDate.getThreadInstance().setName("admin");//为每个线程放置变量                MyThreadScopeDate.getThreadInstance().setAge(data);                new A().get();        new B().get();     }   }).start();}      }static class A{public void get(){ int data=x.get(); System.out.println("A:"+Thread.currentThread().getName()+" has put data:"+data); MyThreadScopeDate obj=MyThreadScopeDate.getThreadInstance(); System.out.println("A"+Thread.currentThread().getName()+":"+obj);}}static class B{public void get(){ int data=x.get(); System.out.println("B:"+Thread.currentThread().getName()+" has put data:"+data); MyThreadScopeDate obj=MyThreadScopeDate.getThreadInstance(); System.out.println("A:"+Thread.currentThread().getName()+":"+obj);}}}//封装的变量对象 ,单例class MyThreadScopeDate{private MyThreadScopeDate(){}//私有化的构造函数不允许实例化private static ThreadLocal<MyThreadScopeDate> map=new ThreadLocal<MyThreadScopeDate>();public static  MyThreadScopeDate getThreadInstance(){MyThreadScopeDate instance=map.get();if(instance == null){instance=new MyThreadScopeDate();map.set(instance);}return instance;}private String name;private  int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "MyThreadScopeDate [name=" + name + ", age=" + age + "]";}}

线程池

多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力;一般来说可以创建三种不同类型的线程池,同时线程池也为我们提供了关闭的方法:
a.创建固定大小 的线程池:一次只能执行三个任务,其他任务等待。 Executors.newFixedThreadPool(3)
b.创建缓存线程池:线程数动态量化,空闲线程自动回收。  Executors.newCachedThreadPool()
c.创建单一线程池:池中只有一个线程,如果线程意外终止就新产生一个。Executors.newSingleThreadExecutor()
d.shutdown();//当所有的任务都运行完后,就关闭线程池
e.shutdownNow();//立刻关闭线程池
package com.thread;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;public class ThreadPool {public static void main(String[] args) {           //ExecutorService threadPool=Executors.newFixedThreadPool(3);//创建固定大小的线程池,这里表明只能同时运行3个线程。   //ExecutorService threadPool=Executors.newCachedThreadPool();//创建缓存线程池,为所有的任务都分配一个线程,自动关闭空闲的线程     ExecutorService threadPool=Executors.newSingleThreadExecutor();//创建单一线程池。     for(int i=1;i<10;i++){//循环往线程池中放10个任务            final int task=i;            threadPool.execute(new Runnable() {//在线程池中放任务    @Override    public void run() {    for(int j=5;j<10;j++){     try {Thread.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}     System.out.println("线程"+Thread.currentThread().getName()+" loop of"+j+"次,在执行第 "+task+"个任务!");    }    }    });         }    System.out.println("提交了10个任务");   //threadPool.shutdown();//当所有的任务都运行完后,就关闭线程池           //threadPool.shutdownNow();//立刻关闭线程池}}

0 0