Java-----多线程同步问题

一、多线程访问临界资源

         临界资源 :多个线程同时访问的资源。
         产生原因:有多个线程在同时访问一个资源,如果一个线程在取值的过程中,时间片又被其他线程抢走了,临界资源问题就产生了,就会产生线程安全问题。
          例如:多个窗口共同出售100张票问题。当第一窗口准备出售第一张票时,电脑卡了,这时窗口二显示第一张票还未售出,所以窗口二将第二张票售出了,此时一窗口电脑好了,继续卖票,将第一张票卖出。这就产生了问题。

二、如何解决线程安全问题

          解决方案:一个线程在访问临界资源的时候,如果给这个资源“上一把锁”,这个时候如果其他线程也要访问这个资源,就得在“锁”外面等待。

语法:
	synchronized(锁) {
		//需要访问临界资源的代码段
	}

	说明:
	a.程序走到代码段中,就用锁来锁住了临界资源,这个时候,其他线程不能执行代码段中的代码,只能在锁外边等待
	b.执行完代码段中的这段代码,会自动解锁。然后剩下的其他线程开始争抢cpu时间片
	c.一定要保证不同的线程看到的是同一把锁,否则同步代码块没有意义

                同步代码块

public class Ticket implements Runnable{
     // 需求:100张
	// 临界资源
     private int ticket = 100;
      @Override
      public void run() {
        while (true) {
          //上锁
        synchronized(this){
            if (ticket < 1) {
              break;
            }
            System.out.println("售票员" + Thread.currentThread().getName() + "售出第"+ticket+"张票");
           ticket--;
       	  }
        }
      }
}

                 同步方法:

//非静态方法
public class Ticket implements Runnable{
     // 需求:100张
	// 临界资源
      private int ticket = 100;
      @Override
      public void run() {
        while (true) {
		   if(!sale()){
                break;
            }
        }
      }
  	 public synchronized boolean sale(){//锁是this
           if (ticket < 1) {
               return false;
            }
            System.out.println("售票员" + Thread.currentThread().getName() + "售出第"+ticket+"张票");
           ticket--;
       		return true;
     }
}
//静态方法
public class Ticket implements Runnable{
     // 需求:100张
	// 临界资源
      private static int ticket = 100;
      @Override
      public void run() {
        while (true) {
		   if(!sale()){
                break;
            }
        }
      }
  	 public synchronized static boolean sale(){ //锁是 类.class
           if (ticket < 1) {
               return false;
            }
            System.out.println("售票员" + Thread.currentThread().getName() + "售出第"+ticket+"张票");
           ticket--;
       	   return true;
     }
}

                   重入锁

                        从jdk1.5之后加入新的接口 Lock,ReentrantLock是Lock接口的实现类。
                       通过显式定义同步锁对象来实现同步,同步锁提供了比synchronized代码块更广泛的锁定操
                       注意:最好将 unlock的操作放到finally块中
                       通过使用ReentrantLock这个类来进行锁的操作,它实现了Lock接口,使用ReentrantLock可以显式地加锁、释放锁

//使用重入锁
private static volatile SingleTon singleTon=null ;
    private static ReentrantLock lock = new ReentrantLock();
    private SingleTon(){
        synchronized (SingleTon.class) {
            if(singleTon!=null){
                throw new RuntimeException("禁止反射破解!");
            }
        }
    }
    public static SingleTon getInstance(){
        lock.lock();
        try {
            if(singleTon==null){
                singleTon = new SingleTon();
            }
            return singleTon;
        }finally {
            lock.unlock();
        }
    }

 二、死锁

每个人都拥有其他人需要的资源,同时又等待其他人拥有的资源,并且每个人在获得所有需要的资源之前都不会放弃已经拥有的资源。

当多个线程完成功能需要同时获取多个共享资源的时候可能会导致死锁。

             死锁的条件:

                1、两个以上的线程

                2、至少两个锁以上

                3、同步中嵌套同步

三、线程通信 

                  在jdk1.5之前有三个方法实现线程通信:

                  wait(): 等待,线程执行这个方法进入等待队列(和锁有关,一个锁对应一个等待队列), 需要被唤醒

                  notify(): 通知唤醒,从等待队列中随机唤醒一个线程

                  notifyAll():全部唤醒,把等待队列中所有的线程都唤醒

                  当多个线程都进入一个等待队列,在使用notifyAll的时候,会全部唤醒,浪费资源。这时我们可以创建多个等待队列,让等待线程分类进入,唤醒的时候只需要唤醒我们需要的队列就行,这样大大提高了效率。

ReentrantLock lock = new ReentrantLock();
        Condition rl = lock.newCondition();   //创建第一个等待队列
        Condition wl = lock.newCondition();   //创建第二个等待队列
        try {
            rl.await();                       //线程放入第一个等待队列
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        wl.signal();                          //唤醒第二个等待队列中的线程

四、线程池     

            为什么需要线程池: 

                         例如有非常的多的任务需要多线程来完成,且每个线程执行时间不会太长,这样会频繁的创建和销毁线程。频繁创建和销毁线程会比较耗性能。如果有了线程池就不要创建更多的线程来完成任务,因为线程可以重用

//创建容量为4的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(4);
//给线程池添加线程
threadPool.submit("线程");
//关闭线程池
threadPool.shutdown();

五、读写锁

ReadWriteLock接口:可实现多个线程"同时"读写操作,

                        读/写    写/写互斥

                        读/读 不互斥

其实现类中ReentrantReadWriteLock 中有readLock和writeLock方法 再实现上锁和解锁

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 您好!如果我理解正确的话,您的问题是关于JMU Java 07多线程同步访问的。以下是我的回答: 多线程编程是指在一个程序中同时执行多个线程的编程技术。多线程编程在提高程序性能、增强程序交互性等方面有很大的优势,但也会引入一些问题,比如线程安全问题。 当多个线程同时访问共享资源时,可能会出现线程不安全的问题,比如数据竞争、死锁等。为了避免这些问题,我们需要使用同步机制来确保线程安全。 Java中提供了多种同步机制,比如synchronized关键字、ReentrantLock类、Semaphore类等。这些同步机制都可以用来保证多线程访问共享资源的安全性。 其中,synchronized是最常用的同步机制之一。在Java中,synchronized关键字可以用来修饰方法或代码块,以实现同步访问共享资源。synchronized可以保证在同一时间只有一个线程可以访问共享资源,从而避免了数据竞争等线程安全问题。 除了synchronized之外,Java还提供了一些其他的同步机制,比如ReentrantLock类和Semaphore类。ReentrantLock类提供了比synchronized更灵活的锁机制,可以实现可重入锁、公平锁等功能。Semaphore类可以控制同时访问共享资源的线程数量,从而避免资源被过度占用的问题。 总的来说,多线程编程是一项复杂而重要的技术,需要仔细研究和实践。在实际开发中,我们需要根据具体的需求选择合适的同步机制来确保多线程访问共享资源的安全性。 ### 回答2: 多线程编程是在当前计算机领域中最为常见的技术之一,它可以利用计算机中的多核处理器来使程序运行更加高效。但是,多线程编程中可能会出现的最大问题就是线程安全,因为线程之间可能会访问相同的资源,从而导致竞态条件。 在Java中,可以通过使用synchronized关键字来实现同步访问,从而避免线程安全问题。synchronized关键字可以用于两种不同的情形:同步方法和同步块。在同步方法中,方法是同步的,即每个线程在执行该方法时都需要获取该对象的锁,如果该锁已经被其他线程获取,则需要等待直到此锁被释放。在同步块中,需要手动指定锁,即每个线程在执行同步块时需要获取该指定锁,其他线程如果需要访问该代码块中的共享资源也需要获取该指定锁,这样就保证了该代码块中的所有共享资源的同步访问。 除了synchronized关键字外,Java还提供了其他一些同步机制来实现线程安全,如ReentrantLock类和CountDownLatch类等。ReentrantLock类可以实现更为灵活的同步访问控制,但需要手动释放锁;而CountDownLatch类则用于同步一个或多个线程,使这些线程在某个条件满足之前一直处于等待状态。 在进行多线程编程时,应该尽量避免对同步访问造成瓶颈,应该通过减小同步代码块的范围等方式来提高程序的效率。此外,多线程编程时还应该进行线程安全性的测试,以确保程序能够正确地运行。 ### 回答3: 在Java中,多线程是一种非常常见的编程方式。由于多线程的特点,对共享资源的访问会出现竞争的情况,这种竞争可能会导致数据不一致或程序异常等问题。因此,在多线程编程中,我们需要采取一些措施来保证共享资源的访问能够正确、有序地进行,这就是同步机制。 同步机制包括两种方式:锁和信号量。锁是最基本的同步机制。锁有两种类型:互斥锁(Mutex)和读写锁(ReadWriteLock)。互斥锁用于保护共享资源,保证同一时间只有一个线程可以访问它,其他线程需要等待锁释放后才能继续访问。读写锁用于读写分离场景,提高了多线程访问共享资源的并发性。读写锁支持多个线程同时读取共享资源,但只允许一个线程写入共享资源。 信号量是一种更加高级的同步机制。信号量可以用来控制并发线程数和限制访问共享资源的最大数量。在Java中,Semaphore类提供了信号量的实现。Semaphore可以控制的线程数量可以是任意的,线程可以一起执行,也可以分批执行。 除了锁和信号量,Java还提供了一些其他同步机制,比如阻塞队列、Condition等。阻塞队列是一种特殊的队列,它支持线程在插入或者删除元素时阻塞等待。Condition是一种锁的增强,它可以让线程在某个特定条件下等待或者唤醒。 在多线程编程中,使用同步机制需要注意以下几点。首先,同步机制要尽可能的保证资源访问的公平性,避免因为某些线程执行时间过长导致其他线程等待时间过长。其次,同步机制要尽可能的避免死锁的发生,尤其要注意线程之间的依赖关系。最后,同步机制的实现要尽可能地简单,避免过于复杂的代码实现带来的维护成本。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值