精华内容
下载资源
问答
  • 在本章,你会学到:线程和多线程技术在iPhone应用中,如何使用锁来编写和管理线程和多线程环境相关的概念1)安全性:程序应该产生我们预期的结果2)活跃性:预期的结果必须最终在某个时间点上产生3)性能:预期的结果...

    在本章,你会学到:线程和多线程技术

    在iPhone应用中,如何使用锁来编写和管理线程

    和多线程环境相关的概念

    1)安全性:程序应该产生我们预期的结果

    2)活跃性:预期的结果必须最终在某个时间点上产生

    3)性能:预期的结果必须较快的产生

    知道什么时候使用线程

    了解线程和苹果内置的其他解决方案的不同

    如今的计算设备已经有越来越多的处理器。iPhone也不例外;在不久的将来,iPhone将会有许多处理器;事实上,Android有些设备已经拥有双核处理器。因此,我们需要知道如何在多处理器上开发程序。这仅仅是我们学习多线程的一个原因。线程这种渠道能够帮助你解决一些其他问题,比如异步代码,文件和网络IO,或者减缓计算过程。

    线程和多线程编程

    简单来说,线程是操作系统运行的一序列命令。不同的线程能够运行在相同的处理器或不同的处理器上,这取决于操作系统,如图6-1。

    正常情况下,操作系统打开一个新的应用程序时,只有一个线程 --  换句话说,从头到尾只有一个命令集。多线程是指,系统有多于一个的命令集在并发情况下运行。在一个多处理器系统中,每一个线程可以在每一个处理器上同时执行,如图6-1 。

    但是,在单个处理器系统中,CPU在切到其他线程之前,会在一个线程中处理某些命令。在图6-2中,在进入线程2之前,CPU在线程1中执行一些命令。然后在进入线程3之前,CPU在线程2中执行命令。

    沿着处理的路径,不同的线程可以从不同的对象中调用不同的方法,如图6-3。线程1和线程2可以同时使用相同的对象调用相同的方法,线程1和线程2也可以调用Object 1的 Method 1和Object 3的Method 3。但是,当线程1调用Object 2的Method 2,线程2调用Object 4的Method 4的时候,线程1和线程2可以调用不同的对象和方法。

    在同一个应用中使用多线程,有优点也有缺点。下面这个部分将会讨论他们。多线程最大的好处是可以将其他任务从main UI中解耦出来,这样UI就不会阻塞或冻结。

    线程术语

    下面是一些重要的线程术语:线程这个术语是用来表示代码中一个独立的命令/可执行序列。

    线程这个术语是用来表示多线程中的一个正在运行的可执行程序。

    线程这个术语是用来表示需要执行的一个抽象的工作。

    线程和进程有什么不同,当他们都用来执行并发逻辑?进程在下面这些方面和线程不同:进程是一个分配单元;它有自己的资源,堆内存和优先级。线程只是一个拥有它自己的栈和程序计算的可执行单元。

    一个进程可以包含很多线程,一个线程只能属于一个进程。

    一个线程可以和属于同一个进程的其他线程共享数据。两个进程之间不能共享数据;他们通常使用进程间通信来转移数据。(进程间通信已经超出本章的范围)

    系统必须分配一个特定的资源,进程不能共享这些资源,所以进程被认为是重量级的。然而,线程可以共享资源,所有系统能够在同一个进程内部创建很多线程。

    展开全文
  • 文章目录1、什么情况下应该使用多线程2、如何应用多线程3、Java 并发编程的基础4、守护线程 1、什么情况下应该使用多线程 线程出现的目的是什么?解决进程中多任务的实时性问题?其实简单来说,也就是解决“阻塞”...

    1、什么情况下应该使用多线程

    线程出现的目的是什么?解决进程中多任务的实时性问题?其实简单来说,也就是解决“阻塞”的问题,阻塞的意思就是程序运行到某个函数或过程后等待某些事件发生而暂时停止 CPU 占用的情况,也就是说会使得 CPU 闲置。还有一些场景就是比如对于一个函数中的运算逻辑的性能问题,我们可以通过多线程的技术,使得一个函数中的多个逻辑运算通过多线程技术达到一个并行执行,从而提升性能,所以,多线程最终解决的就是“等待”的问题。

    简单总结的使用场景
    1)通过并行计算提高程序执行性能
    2)需要等待网络、I/O 响应导致耗费大量的执行时间,可以采用异步线程的方式来减少阻塞。比如如果客户端只有一个线程,这个线程发起读取文件的操作必须等待IO 流返回,线程(客户端)才能做其他的事,那么我们可以使用多线程,一部分线程在等待 IO 操作返回,其他线程可以继续做其他的事。此时从客户端角度来说,客户端没有闲着。

    2、如何应用多线程

    在 Java 中,有多种方式来实现多线程。继承 Thread 类、实现 Runnable 接口、使用 ExecutorService、Callable、Future 实现带返回结果的多线程。
    1)继承 Thread 类创建线程
    Thread 类本质上是实现了 Runnable 接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过 Thread 类的 start()实例方法。start()方法是一个native 方法,它会启动一个新线程,并执行 run()方法。这种方式实现多线程很简单,通过自己的类直接 extend Thread,并复写 run()方法,就可以启动新线程并执行自己定义的 run()方法。

    public class MyThread extends Thread { 
     	public void run() { 
     		System.out.println("MyThread.run()"); 
     	} 
    }
    
    MyThread myThread1 = new MyThread(); 
    MyThread myThread2 = new MyThread(); 
    myThread1.start(); 
    myThread2.start(); 
    

    2)实现 Runnable 接口创建线程
    如果自己的类已经 extends 另一个类,就无法直接 extends Thread,此时,可以实现一个 Runnable 接口

    public class MyThread extends OtherClass implements Runnable { 
     	public void run() { 
     		System.out.println("MyThread.run()"); 
     	} 
    } 
    

    3)实现 Callable 接口通过 FutureTask 包装器来创建Thread 线程
    有的时候,我们可能需要让一步执行的线程在执行完成以后,提供一个返回值给到当前的主线程,主线程需要依赖这个值进行后续的逻辑处理,那么这个时候,就需要用到带返回值的线程了。Java 中提供了这样的实现方式

    public class CallableDemo implements Callable<String> {
    	public static void main(String[] args) throws ExecutionException, 
    		InterruptedException {
    		ExecutorService executorService=
    		Executors.newFixedThreadPool(1);
    		CallableDemo callableDemo=new CallableDemo();
    		Future<String> future=executorService.submit(callableDemo);
    		System.out.println(future.get());
    		executorService.shutdown();
    	}
    	
    	@Override
    	public String call() throws Exception {
    		int a=1;
    		int b=2;
    		System.out.println(a+b);
    		return "执行结果:"+(a+b);
    	}
    }
    

    3、Java 并发编程的基础

    线程作为操作系统调度的最小单元,并且能够让多线程同时执行,极大的提高了程序的性能,在多核环境下的优势更加明显。但是在使用多线程的过程中,如果对它的特性和原理不够理解的话,很容易造成各种问题。
    线程的状态
    Java 线程既然能够创建,那么也势必会被销毁,所以线程是存在生命周期的,那么我们接下来从线程的生命周期开始去了解线程。
    线程一共有 6 种状态(NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING、TERMINATED)

    • NEW:初始状态,线程被构建,但是还没有调用 start 方法
    • RUNNABLED:运行状态,JAVA 线程把操作系统中的就绪和运行两种状态统一称为“运行中”
    • BLOCKED:阻塞状态,表示线程进入等待状态,也就是线程因为某种原因放弃了 CPU 使用权,阻塞也分为几种情况
      Ø 等待阻塞:运行的线程执行 wait 方法,jvm 会把当前线程放入到等待队列
      Ø 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被其他线程锁占用了,那么 jvm 会把当前的线程放入到锁池中
      Ø 其他阻塞:运行的线程执行 Thread.sleep 或者 t.join 方法,或者发出了 I/O请求时,JVM 会把当前线程设置为阻塞状态,当 sleep 结束、join 线程终止、io 处理完毕则线程恢复
    • TIME_WAITING:超时等待状态,超时以后自动返回
    • TERMINATED:终止状态,表示当前线程执行完毕

    在这里插入图片描述
    简单说一下join方法执行原理
    原理如下:
    在这里插入图片描述
    注意:只有先开启了线程,在进行join才有效。
    join方法测试

    • 测试代码
    package com.nrsc.ch1.base;
    
    import com.nrsc.utils.SleepUtils;
    
    public class JoinStudy {
    
        static class LiuNeng implements Runnable {
    
            public void run() {
                System.out.println("LiuNeng开始排队打饭.....");
                SleepUtils.second(2);//休眠2秒
                System.out.println(Thread.currentThread().getName()
                        + " LiuNeng打饭完成.");
            }
        }
    
        static class ZhaoSi implements Runnable {
    
            public void run() {
                SleepUtils.second(2);//休眠2秒
                System.out.println("ZhaoSi开始排队打饭.....");
                System.out.println(Thread.currentThread().getName()
                        + " ZhaoSi打饭完成.");
            }
        }
    
        public static void main(String[] args) throws Exception {
            //for (int i = 0; i < 3; i++) {
                //创建赵四线程
                ZhaoSi zhaoSi = new ZhaoSi();
                Thread zsThread = new Thread(zhaoSi);
    
                //创建刘能线程
                LiuNeng liuNeng = new LiuNeng();
                Thread lnThread = new Thread(liuNeng);
    
                //开启刘能线程
                lnThread.start();
                //刘能开始join --- 注意,只有先开启了线程,join才有效
                lnThread.join();
    
                //开启赵四线程,并join
                zsThread.start();
                zsThread.join();
    
                System.err.println("谢广坤开始排队打饭.....");
                SleepUtils.second(2);//让主线程休眠2秒
                System.err.println(Thread.currentThread().getName() + " 谢广坤打饭完成.");
            }
       // }
    }
    
    • 测试结果

    在这里插入图片描述

    4、守护线程

    在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)
    用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:
    只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
    Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。
    User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。
    值得一提的是,守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。下面的方法就是用来设置守护线程的。

    Thread daemonTread = new Thread();
    // 设定 daemonThread 为 守护线程,default false(非守护线程)
    daemonThread.setDaemon(true);
    // 验证当前线程是否为守护线程,返回 true 则为守护线程
    daemonThread.isDaemon();
    

    这里有几点需要注意:

    (1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。
    (2) 在Daemon线程中产生的新线程也是Daemon的。
    (3) 不要认为所有的应用都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑。

    因为你不可能知道在所有的User完成之前,Daemon是否已经完成了预期的服务任务。一旦User退出了,可能大量数据还没有来得及读入或写出,计算任务也可能多次运行结果不一样。这对程序是毁灭性的。造成这个结果理由已经说过了:一旦所有User Thread离开了,虚拟机也就退出运行了。

    //完成文件输出的守护线程任务
    import java.io.*;
    class TestRunnable implements Runnable{
    	public void run(){
    	  try{
    	    Thread.sleep(1000);//守护线程阻塞1秒后运行
    	    File f=new File("daemon.txt");
    	    FileOutputStream os=new FileOutputStream(f,true);
    	    os.write("daemon".getBytes());
    	  }catch(IOException e1){
    	    e1.printStackTrace();
    	  }catch(InterruptedException e2){
    	    e2.printStackTrace();
    	  }
    	}
    }
     
    public class TestDemo2{
    	public static void main(String[] args) throws InterruptedException
    	{
    		Runnable tr=new TestRunnable();
    		Thread thread=new Thread(tr);
    		thread.setDaemon(true); //设置守护线程
    		thread.start(); //开始执行分进程
    	}
    }
    

    //运行结果:文件daemon.txt中没有"daemon"字符串。
    看到了吧,把输入输出逻辑包装进守护线程多么的可怕,字符串并没有写入指定文件。原因也很简单,直到主线程完成,守护线程仍处于1秒的阻塞状态。这个时候主线程很快就运行完了,虚拟机退出,Daemon停止服务,输出操作自然失败了。

    public class Test {
      public static void main(String args) {
          Thread t1 = new MyCommon();
          Thread t2 = new Thread(new MyDaemon());
          t2.setDaemon(true); //设置为守护线程
          t2.start();
          t1.start();
      }
    }
     
    class MyCommon extends Thread {
      public void run() {
          for (int i = 0; i < 5; i++) {
              System.out.println("线程1第" + i + "次执行!");
              try {
                  Thread.sleep(7);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
    }
    
    class MyDaemon implements Runnable {
      public void run() {
          for (long i = 0; i < 9999999L; i++) {
              System.out.println("后台线程第" + i + "次执行!");
              try {
                  Thread.sleep(7);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
          }
      }
    }
    

    运行结果

    后台线程第0次执行!
    线程10次执行! 
    线程11次执行! 
    后台线程第1次执行! 
    后台线程第2次执行! 
    线程12次执行! 
    线程13次执行! 
    后台线程第3次执行! 
    线程14次执行! 
    后台线程第4次执行! 
    后台线程第5次执行! 
    后台线程第6次执行! 
    后台线程第7次执行! 
    Process finished with exit code 0 
    

    从上面的执行结果可以看出:
    前台线程是保证执行完毕的,后台线程还没有执行完毕就退出了。
    实际上:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,因此,在使用后台县城时候一定要注意这个问题。
    补充说明:

    定义:守护线程–也称“服务线程”,在没有用户线程可服务时会自动离开。
    优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。
    设置:通过setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为守护线程的方式是在 线程对象创建 之前 用线程对象的setDaemon方法。
    example:垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于实时监控和管理系统中的可回收资源。
    生命周期:守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。也就是说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。那Java的守护线程是什么样子的呢。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个或以上的非守护线程则JVM不会退出。

    展开全文
  • 使用多线程技术时不要仅仅因为你会使用多线程技术,你应该有一个更好的想法,比如更好的处理线程安全问题,以及多线程使用成本。尽可能的去测试多线程应用程序的性能以及响应速度,而不是靠猜想。 一、成本 更复杂...


    前言

    从单线程应用程序到多线程应用程序不仅仅需要考虑多线程应用程序更加高效,我们还需要考虑使用多线程的成本。使用多线程技术时不要仅仅因为你会使用多线程技术,你应该有一个更好的想法,比如更好的处理线程安全问题,以及多线程使用成本。尽可能的去测试多线程应用程序的性能以及响应速度,而不是靠猜想。


    一、成本

    • 更复杂的程序设计
    • 上下文切换的开销

    1.1 更复杂的程序设计

    • 在某些情况下,一个多线程应用程序比单线程应用程序更加简单,但是在某些特殊情况下要更加复杂。
    • 在多个线程引用了同一个共享变量的情况下,多线程应用程序比单线程应用程序使用起来更加复杂,因为需要考虑线程安全问题,不正确的线程同步问题出现的问题往往防不胜防。

    1.2 上下文切换的开销

    • 当一个CPU从当前线程切换的执行另外一个线程时,CPU需要保存当前线程的本地数据以及程序指针等等,再有另外一个线程切换到当前线程时,需要重新读取本地数据和程序指针等,这个交换的执行过程叫做上下文切换。
    • 上下文切换的成本并不便宜。

    1.3 减少上下文切换

    • 无锁并发编程:多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些方法来避免使用锁。
    • CAS算法:Java的atomic包使用CAS算法来更新数据,而不需要加锁。
    • 使用最少线程:避免创建不需要的线程。
    • 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。
    展开全文
  • 我们在之前完成的代码,是从main函数以后开始的,通过层层的调用,他只有一个线程,也不会出现一些稀奇古怪的问题,但是如果你使用多线程,就会出现一些有意思的代码。 首选我介绍一些如何创建线程,一共能够有两...

    我想复习一下多线程就来博客整理发出来大家一起努力了

    第一什么是多线程,如何去完成一个多线程

    我们在之前完成的代码,是从main函数以后开始的,通过层层的调用,他只有一个线程,也不会出现一些稀奇古怪的问题,但是如果你使用了多线程,就会出现一些有意思的代码。
    首选我介绍一些如何创建线程,一共能够有两种方式。

    第一种继承Thread

    下面是一个小demo

    public class MM extends Thread {
    	static int count=10;
    	@Override
    	public void run() {
    		for (int i = 0; i < 30; i++) {
    			System.out.println(count++);
    			System.out.println(this.getName());
    		}
    	}
    	public static void main(String[] args) {
    		
    		MM mm=new MM();
    		MM mn=new MM();
    		mm.start();
    		mn.start();
    	}
    }
    

    上面的打印的结果是
    我们我们能够知道的是我们使用了两个线程,将count值加到了69,这就是多线程
    第二种是实现Runnable接口,

    public class NN implements Runnable{
    	static int count=10;
    	@Override
    	public void run() {
    		// TODO Auto-generated method stub
    		for (int i = 0; i < 30; i++) {
    			System.out.println(count++);
    			System.out.println(Thread.currentThread().getName());
    		}
    	}
    public static void main(String[] args) {
    	NN nn=new NN();
    	Thread t1=new Thread(nn);
    	t1.start();
    	NN nn1=new NN();
    	Thread t11=new Thread(nn1);
    	t11.start();
    }
    }
    

    在这里插入图片描述一样的效果但是有细微的区别,但是一般都在使用接口,因为继承只能继承一个,但是可以实现多个接口,

    还有一种,我用的会更多,是内部类的方式

    public class Oo {
    	static int count=0;
    public static void main(String[] args) {
    	for (int i = 0; i < 300; i++) {
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				// TODO Auto-generated method stub
    				for (int i = 0; i < 1000; i++) {
    					System.out.println(count++);
    				}
    				
    			}
    		}).start();
    	}
    
    }
    }
    

    你们可以猜一下答案,我用了300个线程每一个线程加1000,一共是300000,但是结果并不是
    在这里插入图片描述
    在这里插入图片描述
    每一次的结果理论上来说都不一样,但是应该不会等于300000,就是因为他会出现多线程引发的隐患,具体是因为什么原因呢?
    _temp = i;
    i = i + 1;
    i = _temp;
    这就是i++的底层代码,先将值取出,做运算,然后在将计算好的值赋值回去,因为是有多线程,每一个线程是争抢cpu,每个时间片只允许一个线程运行,这样的话就存在一种可能性,一个线程刚刚的加值取出,另一个线程就把值传回去了,没有计算,所以会出现值小于原计划值得存在.

    下面我简单介绍一下线程的生命周期

    线程会有新建、就绪、运行、阻塞和死亡状态。
    1新建状态,创建一个进程具体的可以参考上面我写的
    2就绪状态,新建状态完成,线程被创建以后通过Thread类里面的start()调动,
    3运行状态,当就绪获得时间片以后,从就绪进入运行
    4阻塞状态,当运行状态遇到一些问题的时候调用sleep()方法让线程睡眠,调用wait()方法让线程等待,调用join()方法,或者是用户的一些操作或者是IO流的操作.从阻塞的状态到运行状态也是一样的,等待sleep方法完成,或者是wait方法被notify(),或者是用户输入完成.
    5,死亡状态,run方法运行完毕或者是出现异常

    下面就是一些多线程中的一些常用的方法

    join方法

    当一个线程需要等待另一个线程完毕再执行的话,可以使用Thread的join方法。
    假设线程A和线程B,在A执行时调用了B的join方法,A将被阻塞,一直等到B线程执行完毕后,A线程继续执行,就好像排队加塞。

    
    public class Join {
    public static void main(String[] args) {
    	Maiin mm=new Maiin();
    	mm.start();
    }
    }
    class Maiin extends Thread{
    	@Override
    	public void run() {
    		for (int i = 0; i < 50; i++) {
    			if(i==30) {
    				Maiin1 mm=new Maiin1();
    				mm.setName("我是多线程");
    				mm.start();
    				try {
    					mm.join();
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    			}
    			System.out.println(i+"s"+this.getName());
    		}
    	}
    }
    class Maiin1 extends Thread{
    	@Override
    	public void run() {
    		for (int i = 0; i < 50; i++) {
    			this.setName("我是多线程*2");
    			System.out.println(i+"s"+this.getName());
    		}
    	}
    }
    
    Thread.sleep();

    这个方法就是在指定的毫秒数内让指定的线程暂时休眠,这个是受到系统的一些控制,具体我没有研究的很深,这个线程是静态的方法,就是直接使用Thread.sleep(long millis),里面存放的是毫秒数.
    最关键的是他在sleep的时候不会放弃CPU的执行权力,也就是说他在休眠时没有线程能够使用,这个也不难我就不做过多的叙述了

    消费者生产者问题

    这个是一个很经典的多线程的问题,我来用代码来简述一下
    第一,问题简述,生产者负责生产产品,消费者负责消费产品,产品是放在一个容器里面,容器有容量,容量有限,去用代码去实现这个过程.
    第二,我们可以先完成产品的实现,完成一个Java bean

    public class Product {
    	private int id;
    	private String name;
    	@Override
    	public String toString() {
    		return "Product [id=" + id + ", name=" + name + "]";
    	}
    	public int getId() {
    		return id;
    	}
    	public void setId(int id) {
    		this.id = id;
    	}
    	public String getName() {
    		return name;
    	}
    	public void setName(String name) {
    		this.name = name;
    	}
    	public Product(int id, String name) {
    		super();
    		this.id = id;
    		this.name = name;
    	}
    	public Product() {
    		// TODO Auto-generated constructor stub
    	}
    	
    }
    

    这是一个产品的Java bean 我们要制造和消费的就是这个东西
    第三,我们要开始完成容器,我这里使用的是LinkedList,单独完成一个类里面完成存取的功能,为了方便执行我在里面添加一个方法去打印容器里面的产品的情况

    import java.util.LinkedList;
    
    public class Contain {
     	public static LinkedList<Product> ll=new LinkedList<Product>();
    	public static  int containnum=10;//这个东西就是相当于容器的最大的容量
    	
    	/**
    	 * 这个类添加了synchronized,添加悲观锁,实现了生产者的功能,将产品存到指定的容器中,
    	 * @param p
    	 */
    	public synchronized void put(Product p) {
    		while(ll.size()>10) {
    				try {
    					wait();
    				} catch (InterruptedException e) {
    					// TODO Auto-generated catch block
    					e.printStackTrace();
    				}
    				}
    		
    		ll.add(p);
    		notifyAll();
    	}
    	
    	/**
    	 * 这个类实现了产品的拿出
    	 * @return
    	 */
    	public synchronized Product take() {
    		
    		while(ll.size()==0) {
    			try {
    				wait();
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    		Product removeLast = ll.removeLast();
    		notifyAll();
    		return removeLast;
    	}
    	
    	/**
    	 * 这个功能实现了打印产品中的数量
    	 */
    	public void check() {
    		System.out.println(ll.size());
    	}
    }
    

    第四我们去实现一个类,去完成一下我们的方法,

    
    public class FF {
    	public  static Contain cc=new Contain(); 
    	public static int j=1;
    public static void main(String[] args) {
    	/**
    	 * 去实现拿的方法,达到消费者的角色
    	 */
    	for (int i = 0; i < 3; i++) {
    		new Thread(new Runnable() {
    			
    			@Override
    			public void run() {
    				// TODO Auto-generated method stub
    				while(true) {
    					try {
    						cc.take();
    						Thread.sleep(500);//为了降低打印的速度
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    				}
    				
    			}
    		}).start();
    	}
    	/**
    	 * 这个实现生产者
    	 */
    	for (int i = 0; i < 2; i++) {
    		new Thread(new Runnable() {
    			
    			@Override
    			public void run() {
    				// TODO Auto-generated method stub
    				while(true) {
    					try {
    						Product pp=new Product(j++, "pro"+j++);
    						cc.put(pp);
    						System.out.println("pput");
    						Thread.sleep(300);
    					} catch (InterruptedException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}
    				}
    				
    			}
    		}).start();
    	}
    	/**
    	 * 一直去实现这个打印的方法,一直去实现他的方法
    	 */
    	while(true) {
    		cc.check();	
    	}
    	
    }
    }
    
    

    我们可以通过改变sleep里面的时间来控制生产者生产的速度和消费者消费的速度

    wait notify

    当线程A运行过程中遇到不满足的条件需要等待,等待另外的线程B去更改系统状态。当B更改系统状态后,唤醒等待线程A。等待线程A查看是否满足条件,如果满足则继续执行,不满足则继续等待。
    也就是两者遇到wait需要去等待,等待一个notify去唤醒,这里面有一些的东西,当一个线程被wait的时候,然后,可以用notify或者notifyAll(),来唤醒,但是不一样,如果是notifyAll是唤醒所有的wait,但是notify是随机唤醒一个,由具体是哪一个虚拟机判定的.具体实现是通过设置一个变量来实现的.

    public class Testwait {
    	public static void main(String[] args) {
    		 LoopTest lt = new LoopTest();
    	new Thread(new Runnable() {
    			@Override
    			public void run() {
    				// TODO Auto-generated method stub
    				while (true)
    					lt.f1();
    			}
    		}, "线程A").start();
    	new Thread(new Runnable() {
    			@Override
    			public void run() {
    				// TODO Auto-generated method stub
    				while (true)
    					lt.f2();
    			}
    		}, "线程B").start();
    	}
    }
    class LoopTest {
    	boolean flag = true;
    	public synchronized void f1() {
    		while (flag) {
    			try {
    				wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		} 
    			System.out.println(Thread.currentThread().getName());
    			flag = true;
    			notify();
    	}
    
    public synchronized void f2() {
    		while (!flag) {
    			try {
    				wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		} 
    			System.out.println(Thread.currentThread().getName());
    			flag = false;
    			notify();
    	}
    }
    
    

    具体的原理就是,设置一个boolean值,分别完成两个方法设置上synchronized,是每一个线程独享这个方法,然后判定Boolean的值变量,然后去输出线程的名字,然后就去唤醒另外的一个线程去继续运行,这样就能使用wait和notify,

    Future设计模式

    体现出一种设计模式,Future模式是就想你要去蛋糕店去定制一个,然后你给出了订单,他们完成需要时间牡,这段时间需要去等待,但是前台将订单交给后台以后,就可以去面对更多的顾客,去处理他们的问题,但是制作蛋糕的问题就交给别的人,自己实现了,但是完成的度不好,跟大家交流一下,

    线程安全的定义

    线程安全的概念不容易定义,在《Java 并发编程实践》中,作者做出了如此定义:多个线程访问一个类对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用方法代码不必作其他的协调,这个类的行为仍然是正确的,那么这个类是线程安全的。

    先写到这里吧,我后期在完善

    展开全文
  • 背景 最近在做离线batch任务执行的中间件,目标将线上所有的批任务都接过来,以便Hive向Spark 迁移,对任务整个链路追踪(从开始预执行,到执行引擎...在大量并发提交任务时,会出先proposer不生效的情况,应该用H...
  • 在日常开发中,java.text.DateFormat 应该算是使用频率比较高的一个工具类,经常会使用它 将 Date 对象转换成字符串日期,或者将字符串...给一段多线程并发访问 formatForDay() 方法的示例:示例代码,创建了100个 R...
  • 使用单线程还是多线程问题

    千次阅读 2016-06-01 22:05:26
    对于处理时间短的服务或者启动频率高的要用单线程,相反用...使用多线程编程可以给程序员带来很大的灵活性,同时也使原来需要复杂技巧才能解决  的问题变得容易起来。但是,不应该人为地将编写的程序分成一些碎片,让
  • HashMap多线程并发问题分析 hashmap并发问题的症状: hashmap多线程操作同时调用put()方法后可能导致get()死循环,从而使CPU使用率达到100%,从而使服务器宕机. Java的API文档说HashMap是非线程安全的,应该用...
  • Java进阶之多线程:Runnable接口 + 初始并发问题 文章目录学习狂神的第二天前言一、Runnable接口二、使用步骤1.创建线程2.结果样例一:模拟抢火车票结果样例二:龟兔赛跑结果 前言 众所周知,多线程有三种方法...
  • 性能性能是在应用中使用多线程的主要原因。但是,正如你看到的,如果你使用不当的话,你的应用会有很多问题。你的应用会频繁的崩溃,...根据前面提到的这些问题,如果你的应用需要使用多线程来加速运行的话,你应该...
  • 在日常开发中,java.text.DateFormat 应该算是使用频率比较高的一个工具类,经常会使用它 将 Date 对象转换成字符串日期,或者将字符串日期转化成 Date 对象。先来看一段眼熟的代码: public ...
  • 使用锁带来的问题是锁会引起系统中所有线程一直等待 -- 换句话说,就是死锁。如果你能保证你应用的活跃性,那么死锁应该永远都不会发生。问题想象你有两个线程:A和B。在Astart之前,A一直等待B结束。但是,在线程B...
  • 大家应该都很熟悉,他最核心的两个字就是异步,诚然,它对异步的处理非常的出色,但是异步绝对不等于并发,更不等于线程安全,如果把这几个概念搞混了,错误的使用RxJava,是会来带非常问题的。RxJava与并发首先...
  • 不能涉及任何的业务上的处理,所以事务不应该出现在Dao层中,只能在Service层进行业务处理,但是事务的结束和开始都需要Connection对象,而Connection对象又只能出现在Dao层,为了线程安全,这里在JDBCUtils工具类要...
  • 并发能产生问题的情况是,两个线程都去竞争同一个对象才会产生问题,如果你的静态方法只是简单的逻辑是不会有问题的,但是如果你的线程都是去修改静态变量的值的话,应该是会造成线程问题的总的结论:java是线程安全...
  • ---- 更新下答案 -----查询和实际数据不一致的问题是无法避免的,我的理解题主的意思应该是在更新callback之前有其他用户购买成功的情况会导致购买失败的问题,因此可以通过加锁解决,其实如果异步操作都使用 ...
  • 【1】基本讲解与使用 ReadWriteLock同Lock一样也是一个接口,提供了readLock和writeLock两种锁的操作机制...在没有写操作的时候,两个线程同时读一个资源没有任何问题,所以应该允许线程能在同时读取共享资源。...
  • 如果o的属性发生改变,不影响锁的使用,但是如果o变成另外一个对象,则锁定的对象发生改变,应该避免将锁定对象的引用变成另外的对象二、CAS(无锁优化、自旋锁、乐观锁)什么是CASABA问题unsafe
  • 文章目录并发架构任务执行并发的取消与关闭线程池的使用多线程应该避免的问题避免活跃性问题注意性能并发程序测试并发高阶应用显式锁Java内存模型附录 并发架构 任务执行 为什么需要并发框架 更快的响应、更高的...
  • 为什么使用多线程?这应该学习多线程提到了一个关键问题。多线程技术的使用原因也就是多线程的优点: 提高资源利用:多核处理器的发展是多线程技术发扬光大的前提。硬件的提升使得人们对软件的要求也慢慢的提高。而...
  • 浅谈RxJava与多线程并发

    千次阅读 2017-03-03 15:44:00
    认识RxJava已经有一段时间了,但是一直没有机会在项目中尝试,最近同事在新的项目里引进了RxJava写一些事件处理,在review代码的时候发现了一些和多线程并发相关的问题,所以写了这篇文章。 前言 对于RxJava...
  • Java提供了一套API来支持线程之间的交互。在Object类中提供了一套等待通知的APIwait()notify()notifyAll()此处要注意的是,绝不要在循环外面调用wait()方法。...所以:wait方法应该使用循环模式来调用。按...
  • 对于处理时间短的服务或者启动频率高的要...使用多线程编程可以给程序员带来很大的灵活性,同时也使原来需要复杂技巧才能解决 的问题变得容易起来。但是,不应该人为地将编写的程序分成一些碎片,让这些碎片按 各自...
  • 初级—中阶理解java中较为高级的特性,如反射,动态代理,JVM,内存模型,多线程等等。熟练使用框架,对框架中遇到的bug,能够借助日志和搜索引擎分析出问题的原因。在团队中,能够独立完成普通后台业务功能的...
  • 互斥的问题使用线程的时候是我们必须要注意的。 例如两个线程同时开启,由于业务规则,需要访问同一个对象,要取得该对象 中的数据进行修改。 这样线程对同一个数据进行操作的实例有很,例如银行交易。我们...
  •  使用多线程编程可以给程序员带来很大的灵活性,同时也使原来需要复杂技巧才能解决的问题变得容易起来。不论什么时候只要能用单线程就不用多线程,只有在需要响应时间要求比较高的情况下用多线程。某操作允许并发...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 431
精华内容 172
关键字:

并发问题应该使用多线程