多线程的总结

http://houlinyan.iteye.com/blog/1112535

http://www.mamicode.com/info-detail-517008.html

http://blog.csdn.net/vernonzheng/article/details/8288251

学了很久的java,但是一直对多线程这块知识不是很熟悉,看了以上博客决定来做个多线程总结,首先立个列表: 

      多线程零零碎碎的各种概念知识

    死锁和数据不一致的现象

    synchronized 和 volatile 的区别

    synchtonized 和 Reentrantlock的区别

    

    一.多线程的基本概念

   1.什么是多线程? 多线程的优势是什么? 
         理解多线程前必须先理解什么是线程和进程,程序。简单来说程序是一段代码,进程是执行中的程序,线程是进程中的一个执行流。所以说多线程就是一个进程中的多个执行流。
        多线程的优势 ,我理解有三个:
         1)极大发挥了多cpu的优势,在单个cpu的情况下,多个线程并不是真的同时再运作,而是在交替进行。在多个cpu的情况下,多线程才能做到真正的同步。
         2) 防止阻塞,试想若是只能单线程执行,一个线程的执行速度过慢,那么就会影响其他线程的执行,线程就会很容易陷入阻塞状态。
         3)容易建模,若是一个进程任务非常大,我们可以分成很多个小线程执行任务,会降低任务难度。
  
      
  2.多线程的状态及转换
   感觉这个需要个图就能很清晰的理解转换关系,所以下了个图
     线程状态转换
    3 多线程的调度机制
       1)设置线程的优先级setPriority() ,Thread里三个静态常量专门为优先级设置,分别是 MAX_PRIORITY(最高),NORM_PRIORITY,MIN_PRIORITY(最低)
          ( 小小补充:即使设置了优先级,线程也不一定就是高优先级先运行,这仅是给虚拟机提个建议,嗯虚拟机比较大佬)
       2)线程睡眠Thread.sleep(long millis) ,相当于线程经过一场午睡,闹钟响了继续工作。
       3)线程等待wait(),让线程进入阻塞状态。
       4)线程让步Thread.yield(),将资源让出来给相同或更高优先级线程。
       5)线程唤醒 notify,notifyAll(),唤醒同一个对象的阻塞线程。
            6)线程加入 join (),若A线程中调用了B线程的join ,则A线程陷入等待,直到B线程执行完,回到就绪状态。

    这里我需要总结下sleep() ,wait(),yield()的区别

1. sleep()和wait() 的区别

    微笑sleep()使线程进入休眠状态,经过一定时间后会回到可运行状态(不是运行状态);而wait()被挂起后必须经过notify()或是notifyAll()唤醒才能进入可运行状态。

    微笑sleep()不会释放锁,所以若是A线程陷入睡眠,B线程必须等到A睡醒执行完释放锁才能得到锁;而wait() 会释放锁让其他线程得到锁。

    微笑sleep()和wait()都需要抛出InterruptedException异常,但是sleep()属于Thread类,而wait()属于Object类。

2.sleep()和yield()的区别

    奋斗 若A 线程调用sleep()期间,会释放资源让其他线程(不管优先级)得到资源执行;若是A线程调用了yield()方法,也会释放资源让其他线程执行,但是必须是优先级比A高或是相等优先级的线程,所以若是在这个进程中都是优先级都比A低的线程,那么在执行了yield()之后,A线程可能也会得到资源回到运行状态。

   奋斗 yeild()方法不会抛出InterruptedException 异常,而sleep()会。

   4 多线程的实现

   通常情况下,多线程有两种实现方式

         继承Thread

         实现Runnable

  下面我用代码的方式区分下两者的不同:

public class ImplementsRunnable implements Runnable {


	@Override
	public void run() {
		
		System.out.println("implements Runnable");
		
	}


}
public class ExtendsThread  extends Thread{
	private int flag;
    public ExtendsThread(int flag){
    	this.flag=flag;
    }
	@Override
	public void run() {
		try {
			Thread.sleep(1000);
			System.out.println("extends Thread");
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}
	
}
5 多线程的同步

  在讲同步前我们先来看个造成数据不一致的例子

import java.util.ArrayList;

import annotation.main;

public class DataNoSame  {
    static boolean falg=true;
    static ArrayList<Integer> list=new ArrayList();
   
   
   public static void main(String[] args){
       DataNoSame da=new DataNoSame();
       Thread1 t1=da.new Thread1();
       Thread2 t2=da.new Thread2();
       new Thread(t1).start();
       new Thread(t2).start();
   }
   class Thread1 implements Runnable{

    @Override
    public void run() {
        
            list.add(12);
            if(list.size()>0){
                try {
                 Thread.sleep(1000);
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
                System.out.println(list.get(0));
            }
      }
       
   }
   class Thread2 implements Runnable{

    @Override
    public void run() {
        list=null;
    }
       
   }
}

上述的代码结果是:

Exception in thread "Thread-0" java.lang.NullPointerException
	at thread.DataNoSame$Thread1.run(DataNoSame.java:27)
	at java.lang.Thread.run(Thread.java:745)
最后会抛出个空指针异常,因为Thread1在执行到if语句时被Thread2抢占了cpu 先执行了list=null,Thread2 执行完后将资源重新给了Thread1 ,Thread1 继续执行if后面的语句,这时的list 已经为空了,所以会抛出异常。

 如何解决这个问题呢? 

 接下来就讨论多线程同步的问题了。

 sysnchronized 
  解决多线程的同步的问题一般都会想到synchronized,synchronized 的用法有两个:同步块和同步方法。
  同步块:
public class ThreadSync extends Thread{
	private int no;
	//private static Object lock=new Object();
	public  ThreadSync(int no){
		this.no=no;
	}
	public void run(){
		
		synchronized (ThreadSync.class) {
			for (int i = 0; i < 4; i++) {
				try {
					Thread.sleep(1000);
					System.out.println("Thread" + no +"    "+ i);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}
		}
	}
        同步方法:
	private static  synchronized void fun(int no){
		for (int i = 1 ; i < 4; i++) {
			try {
				//Thread.sleep(1000);
				System.out.println("Thread" + no +"    "+ i);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

synchornized是持有对象锁,单一个线程获得一个对象锁则另一个线程等这个线程结束才能获得锁得到执行。这样借用synchronied 就能解决上述的数据不一致情况了。
    volatile
或许很多童鞋都对synchronized熟悉,但是对volatile陌生,(我也是最近才认识了volatile安静)。讲volatile之前先认识下线程对变量的读取,A线程为了提高效率将主存的某成员变量读取并拷贝一份,A线程每次需要用到变量则访问拷贝的数据,B线程改变了主存的这个变量,而A线程访问的是拷贝的数据,所以得不到改变后的数据。所以也会造成数据不一致的情况。
    为了避免数据不一致的情况,可以用volatile修饰变量来强制要求线程每次访问数据都是从主存中访问。
    volatile的特点:
    1)可见性:保证变量的可见性 ;
    2)禁止指令重排性
    3)volatile不能保证原子操作性,所谓原子性是要么代码能一次全部执行,要么都不执行;

public class volatileTest {
   
    
    
    public static void main(String[] args) throws InterruptedException{
        
        Thread1[] t1=new Thread1[100];
        Thread2[] t2=new Thread2[100];
        for(int i=0;i<100;i++){
            t1[i]=new Thread1();
            t2[i]=new Thread2();
        }
        for(int i=0;i<100;i++){
            new Thread(t1[i]).start();
            new Thread(t2[i]).start();
        }
        System.out.println("count="+Thread1.count);
        System.out.println("count2="+Thread2.count2);
        
       
    }
    
    
    
   

}

class Thread1 implements Runnable{
  volatile static int count=0;
    @Override
    public void run() {
        
        for(int i=0;i<100;i++){
            count++;
        }
     
    }
    
}

class Thread2 implements Runnable{
    static int count2=0;
    @Override
    public void run() {
        synchronized(this){
            for(int i=0;i<100;i++){
                count2++;
            }
        }
        
    }
    
}
写了个代码来区分synchronized和volatile的区别
      synchronized 具有可见性和原子操作性,使线程安全的;而volatile 具有可见性,不具有原子操作性,所以是线程不安全的;
      synchronized 能修饰代码块和方法,volatile修饰变量;
      volatile是变量在多线程之间的可见性,synchronized 是多线程之间的可见性。
 
ReentrantLock
最后在总结下Reentrantlock 和synchronized的区别:
   
      1)ReentrantLock 具有synchronized 的相同的并发性和内存语义,还多了定时锁等候和中断锁等候,锁投票等;
      假设有两个线程,线程A得到锁 ,线程B等待锁
      如果使用了synchorinized ,则B一直等待;
      如果使用了ReentrantLock,则B等待一段时间,可中断等待;
       2)synchronized 是在jvm层面上执行的,若是发生异常,jvm会自动释放锁定,而ReentrantLock 是代码执行的,必须在finally释放锁定;
        3)在资源竞争不是很激烈的情况下,使用synchronized 的性能更好,但是在资源竞争激烈的情况下,synchronized 的性能会大幅度下降,而ReentreantLock 的性能维持常态。


   分别用synchronized 和ReentreantLock 写了个生产者和消费者的代码
package thread;
/**
 * 生产者和消费者问题
 * synchronized 实现
 */
import java.util.ArrayList;

public class _1Produdcer_Customer {
     private static ArrayList
     
     
      
       list=new ArrayList();
    final static Integer producter=1;
    final static  Integer customer=2;
     final static private  int NUM=10;
    static  class Producer implements Runnable{
       private String name;
       private int count=0;
       public Producer(String name) {
        super();
        this.name = name;
    }

    public void production(){
       synchronized (producter) {
      
            if (list.size() < NUM) {
              
                    list.add(this.name + " " + ++count+ "0001");
                    System.out.println(this.name + " produced " + count + "0001");
                    producter.notifyAll();//唤醒等待生产的消费者
                
            } else {
                try {
                    System.out.println("生产已满 ,等待消费");
                    producter.wait();

                } catch (InterruptedException e) {

                    e.printStackTrace();
                }
            }
        
    }
       
    }
       
        @Override
        public void run() {
            production();
        }
         
     }
     
     
     static class Customer implements Runnable{
         private String name;
         private int count=0;
         public Customer(String name) {
          super();
          this.name = name;
      }

       public void customtion(){
          
         synchronized (customer) {
             try {
                Thread.sleep(500);
            } catch (InterruptedException e1) {
               
                e1.printStackTrace();
            }
           
                if (list.size() > 0) {
                    System.out.println(this.name + "customed " + list.remove(0));
                   customer.notifyAll();//唤醒等待消费的生产者
                } else {
                    try {
                        System.out.println("消费完毕 ,等待生产");
                        customer.wait();

                    } catch (InterruptedException e) {

                        e.printStackTrace();
                    }
                }
            
        }
         
      }
         
          @Override
          public void run() {
              customtion();
          }
           
       }
     
     
     
     public static void main(String[] args){
         Producer[] p=new Producer[10];
         Customer[] c=new Customer[10];
         for(int i=0;i<10;i++){
             p[i]=new Producer("生产者"+i);
             c[i]=new Customer("消费者"+i);
         }
         for(int i=0;i<10;i++){
             new Thread(p[i]).start();
             new Thread(c[i]).start();
         }
     }
}

     
     
package thread;
/**
 * 生产者和消费者问题
 * ReentrantLock,condition 实现
 */
import java.util.ArrayList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class _2Produdcer_Customer {
     private static ArrayList
      
      
       
        list=new ArrayList();
     final static  Lock lock=new ReentrantLock();
     final static Condition notEmpty=lock.newCondition();
     final static Condition notFull=lock.newCondition();
     final static private  int NUM=10;
    static  class Producer implements Runnable{
       private String name;
       private int count=0;
       public Producer(String name) {
        super();
        this.name = name;
    }

    public void production(){
            lock.lock();
            
            try {
                if (list.size() < NUM) {

                    list.add(this.name + " " + ++count + "0001");
                    System.out.println(this.name + " produced " + count + "0001");
                    notEmpty.signalAll();

                } else {
                System.out.println("生产已满 ,等待消费");
                notFull.await();
                } 
            }catch(Exception e){
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
    }
       
        @Override
        public void run() {
            production();
           
        }
         
     }
     
     
     static class Customer implements Runnable{
         private String name;
         private int count=0;
         public Customer(String name) {
          super();
          this.name = name;
      }

       public void customtion(){
           lock.lock();
           try{
               Thread.sleep(500);
               if (list.size() > 0) {
                   System.out.println(this.name + "customed " + list.remove(0));
                   notFull.signalAll();
               } else {
                    System.out.println("消费完毕 ,等待生产");
                   notEmpty.await();
               }
           }catch(Exception e){
               e.printStackTrace();
           }finally{
               lock.unlock();
           }
        }

    @Override
    public void run() {
        customtion();
    }

  
 }
     
     
     
     public static void main(String[] args){
         Producer[] p=new Producer[10];
         Customer[] c=new Customer[10];
         for(int i=0;i<10;i++){
             p[i]=new Producer("生产者"+i);
             c[i]=new Customer("消费者"+i);
         }
         for(int i=0;i<10;i++){
             new Thread(p[i]).start();
             new Thread(c[i]).start();
         }
     }
}

      
      






   




    

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值