使用多线程和并发编程

Posted zj-blog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用多线程和并发编程相关的知识,希望对你有一定的参考价值。

一.先初步了解一下基本的概念

进程:在一个操作系统中,每个独立执行的程序都可以是一个进程。

线程:一个程序至少有一个进程,一个进程至少有一个线程,java里有一个主线程和垃圾回收线程。

线程的3中创建方式:

 1.继承Thread类

 2.实现Runnable接口

 3.实现Callable接口,和Future、线程池一起使用

线程的优先级:

 优先级的返回是1-10,默认是5,数越大优先级越高。

join的作用是:

 等待该线程终止,指的是主线程等待子线程的终止。子线程调用了join方法,只有子线程执行完之后才会调用主线程。(主线程需要用到子线程的处理结果是使用join)

二.多线程之间的状态转换

技术分享图片

状态之间装换的demo:

这个例子里面涉及到了线程的创建,运行,堵塞,等待,锁池等状态,主要是为了加深自己对状态的理解。

package cn;
public class ThreadDemo extends Thread {
        //标识
	private boolean runningFlag=false;
	
	public ThreadDemo(){
		runningFlag = false;
	}	
	public synchronized void setRunningFlag(boolean runningFlag) {
	this.runningFlag = runningFlag;  
        if(runningFlag){  
            this.notify();  
        }else{  
            try {  
                System.out.println("线程"+Thread.currentThread().getName()+"开始等待");  
                this.wait();  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
	}

	@Override
	public void run() {
		sayHello();
	}
	
	//锁池状态
	public synchronized void sayHello(){
		 while(true){  
	            if(!runningFlag){  
	                try {  
	                    System.out.println("线程"+Thread.currentThread().getName()+"开始等待");  
	                    this.wait();//等待状态  
	                } catch (InterruptedException e) {  
	                    // TODO Auto-generated catch block  
	                    e.printStackTrace();  
	                }  
	            }else{  
	                try {  
	                    sleep(3000);//堵塞状态
	                    System.out.println("线程"+Thread.currentThread().getName()+"任务完成
");    
	                    setRunningFlag(false);        //让当前线程处于等待任务状态  
	                } catch (InterruptedException e) {  
	                    e.printStackTrace();  
	                }  
	            }  
	        }  
	}	
	public static void main(String[] args) {
		int i=10;
		while (i-->0) {
			//新建创建
			ThreadDemo demo=new ThreadDemo();
			demo.setName("demo"+i);
			demo.setRunningFlag(true);
			//可运行状态,start之后等待cpu获取时间片
			demo.start();
		}
		
	}
    
}

三.使用4个线程分别计算1-25,26-50,51-75,76-100的值,最后求和。

 实现思路:定义4个线程分别计算每份数据的和,然后把求的和加入到集合中,最后对集合中的值进行相加。

 使用技术:使用ExecutorService、Callable、Future实现有返回结果的多线程。

 shutdown:调用后,不可以再submit新的task,已经submit的将继续执行。

shutdownNow:试图停止当前正执行的task,并返回尚未执行的task的list

package cn;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ThreadAddFuture {
	
	//创建多个有返回值的任务
	public static  List<Future> futureList=new ArrayList<Future>();
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		
		int sum=0;
		
		int taskSize=4;
		ThreadAddFuture add=new ThreadAddFuture();	
		//创建一个线程池有4个
		ExecutorService pool=Executors.newFixedThreadPool(taskSize);
		
		for (int i =1; i <=76;) {
			
			ThreadTest thread=add.new ThreadTest(i,i+24);
			
			//执行任务并获取Future对象
            Future<Integer> future=pool.submit(thread);
            
            //添加到futureList
            futureList.add(future);
            
            //正好实现4个线程分别计算1-25|26-50|51-75|76-100的和
            i+=25;
		}
		
		//获取所有并发任务的返回结果
		 if(futureList!=null && futureList.size()>0){
			 
	            for(Future<Integer> future:futureList){
	            	
	               sum+=(Integer)future.get();
	               
	            }
	        }
		 
	     System.out.println("total result: "+sum);
	     
	     //关闭线程池
	     pool.shutdown();
	}
	
	//实现Callable接口
	class ThreadTest implements Callable<Integer>{
		
		private int begin;		
		private int end;		
		private int sum=0;
		
		public ThreadTest(int begin,int end){
			this.begin=begin;
			this.end=end;
		}
		
		@Override
		public Integer call() throws Exception {
			for (int i =begin; i <=end; i++) {
				sum+=i;
			}
			 System.out.println("from "+Thread.currentThread().getName()+" sum="+sum);
			return sum;
		}
		
	}
}

四.使用runnable、CountDownLatch、线程池的demo

CountDownLatch:CountDownLatch是一个同步的辅助器是一个计数器,只要计数器为0主线程就可以结束堵塞进行执行,和join很像但是比join更加灵活。

await:await() 方法会一直阻塞直到计数器为0,主线程才会继续往下执行。

countDown():countDown() 方法将计数器减1。

CountDownLatch和join的区别:

调用thread.join() 方法必须等thread 执行完毕,当前线程才能继续往下执行,而CountDownLatch通过计数器提供了更灵活的控制,只要检测到计数器为0当前线程就可以往下执行而不用管相应的thread是否执行完毕。

 

package cn;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class WatchThread {
	
  private String name=UUID.randomUUID().toString();
  
  public void testThread() throws InterruptedException{
	  int threadNum=10;
	  //创建计数器
	  CountDownLatch threadSignal=new CountDownLatch(threadNum);
//创建线程池 ExecutorService executor=Executors.newFixedThreadPool(threadNum); for (int i = 0; i <threadNum; i++) { TestThread task=new TestThread(threadSignal); //执行任务 executor.execute(task); }
//堵塞线程 threadSignal.await();
//关闭线程池 executor.shutdown();
System.out.println(Thread.currentThread().getName() + "+++++++结束."); } public static void main(String[] args) throws InterruptedException{ WatchThread test=new WatchThread(); test.testThread(); } private class TestThread implements Runnable{ private CountDownLatch threadsSignal; public TestThread(CountDownLatch threadsSignal){ this.threadsSignal=threadsSignal; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "开始..." + name); System.out.println("开始了线程::::" + threadsSignal.getCount()); threadsSignal.countDown();//必须等核心处理逻辑处理完成后才可以减1 System.out.println(Thread.currentThread().getName() + "结束. 还有" + threadsSignal.getCount() + " 个线程"); } } }

五.模拟实现消息的发送,一个服务端,一个客户端。

实现思路:

   发送的话,要保证信息100%的发送给客户端,那么发给客户端之后,客户端返回一个消息告诉服务器,已经收到。当服务器一直没有收到客户端返回的消息,那么服务器会一直发     送这个信息,直到客户端发送回确认信息,这时候再删除重复发送的这个信息。

   在服务端创建一个ConcurrentHashMap,当客户端正确接收服务端发送的数据并返回成功标识,从ConcurrentHashMap中删除成功的消息。

   1.创建PushThread类向客户端发送数据

package cn1;

import java.util.Map.Entry;

public class PushThread extends Thread{

	//发送代码,是不断遍历内存对象councurrenthashmap,从中取出信息,不断的重发
	@Override
	public void run() {
		try {
			//重发消息
            for(Entry<Integer,String> hashMap:MainThread.pushmessage.entrySet()){
                System.out.println("消息id:"+hashMap.getKey()+"未发送成功,在此重发:"+hashMap.getValue());
            }
            sleep(1000);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

2.创建RemoveThread类接收客户端返回的数据,成功从ConcurrentHashMap中删除消息

package cn1;

import java.util.Map.Entry;

public class RemoveThread extends Thread {

	//循环的接收客户端返回的消息,返回成功从concurrentHashMap中删除
	@Override
    public void run() {
        try {
            for (int i = 0; i < 10000; i++) {
                sleep(2000);
                for(Entry<Integer, String> map:MainThread.pushmessage.entrySet()){
                    if (map.getKey()==i) {
                        System.out.println("成功收到id为:"+map.getKey()+"返回的信息,删除该元素");
                        MainThread.pushmessage.remove(map.getKey());
                    }
                }
                System.out.println("内存对象中的元素数量为:"+MainThread.pushmessage.size());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 3.创建MainThread类,向concurrentHashMap中添加测试数据

package cn1;

import java.util.concurrent.ConcurrentHashMap;

public class MainThread {
	
	   /**
	    * 消息的发送,一个是服务器,一个是客户端。发送的话,要保证信息100%的发送给客户端,那么发给客户端之后,客户端返回一个消息告诉服务器,已经收到。当服务器一直没有收到客户端返回的消息,那么服务器会一直发送这个信息,直到客户端发送回确认信息,这时候再删除重复发送的这个信息。
	    */
	   public static ConcurrentHashMap<Integer, String> pushmessage=new ConcurrentHashMap<Integer,String>();
	    public static void main(String[] args) {
	        for (int i = 0; i < 10; i++) {
	            pushmessage.put(i, "该消息是id为"+i+"的消息");
	        }
	        Thread pushThread=new PushThread();
	        Thread remove=new RemoveThread();
	        pushThread.start();
	        remove.start();
	        for (int i = 10; i < 20; i++) {
	            pushmessage.put(i, "又一波到来,消息是id为"+i+"的消息");
	        }
	    }
}

六.synchronized的实现原理

 synchronized实现同步的基础:java中的每一个对象都可以是锁,主要有3种形式:

       . 对于普通同步方法,锁的是实例对象。

       .对于静态同步方法,锁的是当前类的Class对象。

       . 对于同步代码块,锁的是括号里配置的对象。

       JVM是基于进入和退出Moniter对象来实现同步和代码块同步的,同步代码块是基于monitorenter和monitorexit实现的,同步方法ACC_synchronized实现的,monitorenter放在同步代码块的开始位置,monitorexit放在同步代码块的结束位置,必须是成对出现的。当前一个moniter被持有后,它将处于锁定状态,线程执行到moniteorenter时,获得monitor的所有权,所得对象的锁。

        synchronized获得的锁是存放在对象头里的,堆中的对象由对象头,实例变量和填充数据组成。对象头的markword存储的是HashCode、分代年龄和锁标记位,锁标记位有无锁状态、偏向锁、轻量级锁、重量级锁。对象头还会存储是否是偏向锁的标识。 

 





以上是关于使用多线程和并发编程的主要内容,如果未能解决你的问题,请参考以下文章

多个请求是多线程吗

并发编程解惑之线程

JUC并发编程 共享模式之工具 JUC CountdownLatch(倒计时锁) -- CountdownLatch应用(等待多个线程准备完毕( 可以覆盖上次的打印内)等待多个远程调用结束)(代码片段

转:Java并发编程之八:多线程环境中安全使用集合API(含代码)

多个用户访问同一段代码

python stackless 怎么多线程并发