zoukankan      html  css  js  c++  java
  • 同步(解决多线程安全问题)

    1.创建线程的两种方式。

      (1)继承Thread(该类就定义了一个功能,用于存储线程要运行的代码,该存储功能就是run方法也就是说Thread类中的run方法,用于存储线程要运行的代码。


      
    (2)实现Runnable(Runable中只定义了一个抽象方法,public void run();)

        步骤:
        1,定义类实现Runable接口
        2,覆盖Runable接口中的run方法。
        将线程要运行的代码存放在该run方法中。
        3,通过Thread类建立线程对象。
        4,将Runable接口的子类对象作为实际参数传递给Thread类的构造函数。
          为什么要将Runable接口的子类对象传递给Thread类的构造函数。
          因为,自定义run方法所属的对象是Runable接口的子类对象。所以要让线程去指定 指定对象的run方法。就必须明确该run方法所属的对象。
        5,调用Thread类的start方法开启线程并调用Runable接口的子类的run方法。
          在Thread类中提供了public Thread(Runnable target)和public Thread(Runnable target,String name)两种构造方法


        两种方式的区别?

        继承Thread:线程代码存放在Thread子类run方法中
        实现Runable:线程代码存在接口的子类的run方法。
        Thread类也是Runnable的子类。也就是说,Thread和Runnable的子类都同时实现了Runnable接口,之后将Runnable的子类实例放到了Thread类之中。
        Thread类和Runnable接口使用上的区别,如果一个类继承Thread类,则不适合于多个线程共享资源,实现了Runnable接口,就可以方便地实现资源的共享。
     
    2.多线程的运行出现了安全问题。
    问题的原因:
    当多条语句在操作同一个线程共享数据时,一个线程对多条语句执行了一部分,还没执行完,另一线程参与进来执行。导致共享数据的错误。
     
    解决办法:
    对多条操作共享数据的语句,只能让一个线程都执行完,再执行过程中,其他线程不可以参与运行。
     
    java对于多线程的安全问题,提供了专业的解决方式,就是同步代码块。
     
    同步代码块格式:
      synchronized(对象1){
          需要同步的代码
        }
    这个里面的对象1就如同锁,持有锁的线程,可以在同步中执行需要同步的代码
    没有持有锁的线程即使获得CPU的执行权,也进不去,因为没有获取锁。
     
    同步的前提:
    (1)必须要有两个或两个以上的线程。
    (2)必须有多个线程并使用同一个锁
     
    必须保证同步中只能有一个线程在运行。
     
     synchronized会自动释放锁
     
    同步的好处和弊端:
    好处:解决了线程的安全问题。
    弊端:同步外的多个线程都需要判断锁,(这些是无效判断)较为消耗资源。
    package org.lxh.demo9.threaddemo;
    class Ticket implements Runnable{
        private  int tick=5;
        public void run(){
            while (true){
                synchronized(this){  //this是一个锁。
                    if (tick>0){
                    try{
                        Thread.sleep(500);
                    }
                    catch(Exception e){
    
                    }
                    System.out.println(Thread.currentThread().getName()+"sale..."+tick--);
                }
            }
                }
    
    } 
    
    public class TicketDemo {
    
        public static void main(String[] args) {
            Ticket t=new Ticket();
            Thread t1=new Thread(t);//创建了一个线程
            Thread t2=new Thread(t);//创建了一个线程
            Thread t3=new Thread(t);//创建了一个线程
            t1.start();
            t2.start();
            t3.start();
        }
    
    } 

    3.死锁

     常见情景之一:同步的嵌套

    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();
    	}
    }
    

      运行结果:Thread1..else lockb...

           Thread0..if locka...

    可以看到线程1拿到了lockb锁,而线程0拿到了locka锁,之后就发生了死锁,因为线程1拿到lockb锁之后,还要想往下运行,需要拿到locka锁,而locka锁在线程0那里;线程0拿到locka锁,想往下执行,需要拿到locka锁,但是lockb锁在线程1那里。这样就形成了死锁。

     4.同步和异步有何异同,在什么情况下分别使用它们?
      如果数据将在线程间共享,例如,正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须同步存取。同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;
      当应用程序在对象上调用一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程。异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。
      同步是阻塞模式,异步是非阻塞模式
     
  • 相关阅读:
    linux下详解shell中>/dev/null 2>&1
    关于使用sublime的一些报错异常退出的解决方法
    Linux下如何挂载文件,并设置开机自动挂载
    关于/var/log/maillog 时间和系统时间不对应的问题 -- 我出现的是日志时间比系统时间慢12个小时
    如何在含有json类型的字段上建立多列索引
    文件大小
    SVN
    索引
    MD5验证
    协议适配器错误的问题
  • 原文地址:https://www.cnblogs.com/GumpYan/p/5744393.html
Copyright © 2011-2022 走看看