多线程基础篇

多线程

进程和线程

区别

  1. 定义:(1) 单核处理器相当于一个CPU = 一个车间,多核处理器相当于多个CPU = 一座工厂 (一座工厂包含多个车间)每个车间都相当于一个进程,进程是程序的一次执行过程,是程序在执行过程汇总分配和管理资源的基本单位,是操作系统资源分配的基本单位,每个进程都有自己的地址空间;(2) 每个车间都有工人,每个工人都相当于一个线程,线程是CPU调度和执行的基本单位;
  2. 联系:线程是进程的一部分,一个线程只能存在于一个进程内,而一个进程有多个线程,并且至少有一个线程在操作系统中能够同时运行多个进程,同一个进程可以有多个线程同时执行
  3. 开销:进程有独立的代码和数据空间(程序上下文切换),进程(程序)切换开销较大;线程可以看作是轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换开销小
  4. 内存分配:系统在运行的时候会为每个进程分配不同的内存空间;对线程而言,系统不会为线程分配空间(线程所具有空间来源于所属进程),线程之间只能共享资源

线程状态

  1. 新建:新建一个线程对象

  2. 就绪:线程被创建后,被其他线程调用start()方法后,该状态线程可以位于运行线程池汇总,等待被线程调度选中,获取CPU的使用权

  3. 运行:就绪状态的线程获取了CPU的时间片,执行程序代码

  4. 阻塞:运行中的线程遇到某些事件放弃CPU使用权,暂时停止运行。直到线程重新进入就绪状态,才有机会获取CPU使用权进入运行状态。阻塞类别:

    4.1 等待阻塞:运行线程遇到wait()方法,JVM会把线程放入等待队列,直到遇到notify(),或notifyAll()才会被唤醒进入就绪状态

    4.2 同步阻塞 :获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把此线程放入锁池,等待其他线程释放锁,本线程由就绪到运行抢占锁后继续运行

    4.3 其他阻塞:运行线程遇到sleep()或join()或发出I/O请求时,JVM会把线程置为阻塞状态。当sleep()或join()超时或I/O处理完毕,线程重新进入就绪状态

  5. 死亡:线程执行结束或者因异常退出

线程的流程图

在这里插入图片描述

创建线程的四种方式

  1. 继承Thread类

    public class ThreadTest {
    
      public static void main(String[] args) {
    	Demo demo = new Demo();
    	demo.start();
      }
     static class Demo extends Thread{
    	  public void run(){
    		  System.out.println("继承Thread创建线程");
    	  }
      }
    }
    
  2. 实现Runnale接口

    public class RunnableTest {
    
    	public static void main(String[] args) {
    		Demo demo = new Demo();
    		Thread thread = new Thread(demo);
    		thread.start();
    
    	}
    
    	static class Demo implements Runnable {
    
    		@Override
    		public void run() {
    			System.out.println("实现Runnable接口创建线程");
    		}
    	}
    }
    
  3. 实现Callable接

    (1) 创建一个类实现Callable接口

    (2) 重写call()方法,注意有返回值

    (3) 创建FutureTask,调用该对象的get()方法可获取call()方法的返回值

    
    public class CallableTest {
    
    	public static void main(String[] args) {
    		FutureTask<String> futureTask = new FutureTask<>(new Demo("继承Callable创建线程"));
    		Thread thread = new Thread(futureTask);
    		thread.start();
    		try {
    			String string = futureTask.get();
    			System.out.println(string);
    		} catch (InterruptedException | ExecutionException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    
    	}
    
    	static class Demo implements Callable<String> {
    
    		private String str;
    
    		public Demo(String str) {
    			this.str = str;
    		}
    
    		@Override
    		public String call() throws Exception {
    
    			return str;
    		}
    
    	}
    }
    
    
  4. 使用线程池创建

    //使用Executors框架创建线程
    public class ThreadPool {
     
    	public static void main(String[] args) {
    
    //		ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();//不重复线程
    //		ExecutorService newCachedThreadPool = Executors.newSingleThreadExecutor();//一个线程
    		ExecutorService newCachedThreadPool = Executors.newFixedThreadPool(2);//2个线程执行
    		
    			newCachedThreadPool.execute(new WorkTask());
    			Future submit = newCachedThreadPool.submit(new Demo());
    			try {
    				Object object = submit.get();
    				System.out.println("实现Callable接口 创建线程:" + object);
    			} catch (InterruptedException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			} catch (ExecutionException e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    			
    		newCachedThreadPool.shutdown();
     
    	}
    	//实现Runnable创建线程
    	static class WorkTask implements Runnable{
    
    		@Override
    		public void run()  {
    		 
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			System.out.println(Thread.currentThread().getName()+ "输出结果");
    		}
    		
    	}
    	//实现Callable创建线程
    	static class Demo implements Callable{
    
    	 
    		@Override
    		public String call() throws Exception {
    			try {
    				Thread.sleep(1000);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    				String str = Thread.currentThread().getName()+ "输出结果";
    			return 	str;
    		}
    		
    	}
    }
    
    
    //使用ThreadPoolExecutor框架创建线程
    public class ThreadPoolTest {
    
    	public static void main(String[] args) {
    		
    		//创建线程池  int corePoolSize(线程池大小) int maximumPoolSize(线程池最大容量) keepAliveTime(多久没有任务执行就关闭线程) workQueue(一个阻塞队列,用来存储等待执行的任务)
    		ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
    		
    		//计数器 (目的:子线程执行完再执行主线程)new CountDownLatch(2)判定有俩线程计数为2
    		CountDownLatch latch = new CountDownLatch(2);
    		
    		executor.execute(new Runnable() {
    			@Override
    			public void run() {
    				try {
    				System.out.println(Thread.currentThread().getName() + "子线程1");
    					
    				} catch (Exception e) {
    					
    				}finally {
                        //计数器减一
    					latch.countDown();	
    			}
    			}
    		});
    		executor.execute(new Runnable() {
    			@Override
    			public void run() {
    				try {
    				System.out.println(Thread.currentThread().getName() + "子线程2");
    					
    				} catch (Exception e) {
    					 
    				}finally {
                        //计数器减一
    					latch.countDown();	
     			}
    			}
    		});
    		try {
                //主线程等待,直到子线程结束,latch为0后主线程再执行
    			latch.await();
    		} catch (InterruptedException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
     
    		executor.shutdown();
    		System.out.println("主线程");
    	}
    
    }
    
    

线程通信

线程通信的模型主要分两种:共享内存和消息传递

方式一:volatile

基于volatile关键字实现线程通讯是体现共享内存的思想。多个线程监听一个变量,当这个变量发生变化时,线程能感知并执行相应业务。备注:VOLATILE 关键字并不能保证线程的安全性

public class VolatileTest {

	private volatile static int temp = 10;

	public static void main(String[] args) {
		Thread A = new Thread(new ThreadA());
		Thread B = new Thread(new ThreadB());
		A.start();
		B.start();
	}

	static class ThreadA implements Runnable {

		@Override
		public void run() {
			while (temp > 0) {
				System.out.println("线程A:" + Thread.currentThread().getName() + "正在执行");
				temp--;
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}

	}

	static class ThreadB implements Runnable {

		@Override
		public void run() {

			while (true) {
				if (temp == 5) {
					System.out.println("线程B:" + Thread.currentThread().getName() + "响应线程A后执行");
					break;
				}
			}
		}
	}
}

方式二:wait() 和 notify()

注意:wait()方法会释放锁,notify()不会释放锁,会继续执行完知道结束。被唤醒后继续执行wait()方法后内容,但共享变量会取主内存值。这俩都是Object类中的方法,在调用这俩方法前必须先获得锁,这限制其使用场合,必须在同步代码块中。其次,当等待队列有多个线程时,notify()只能随机唤醒一个线程,不能唤醒指定线程。还有就是必须wait在前notify在后才能正常唤醒。

public class VolatileTest {

	private volatile static int temp = 10;

	static Object ob = new Object();

	public static void main(String[] args) {
		Thread A = new Thread(new ThreadA());
		Thread B = new Thread(new ThreadB());
		A.start();
		B.start();
	}

	static class ThreadA implements Runnable {
	 
		@Override
		public void run() {
			synchronized (ob) {
				while (temp > 0) {
					System.out.println("线程A获得锁:" + Thread.currentThread().getName() + "正在执行temp--" + temp );
					temp--;
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					if (temp == 5) {
						try {
							System.out.println("线程A即将等待");
							ob.wait();
							System.out.println("线程A被线程B唤醒,此时temp = " + temp );

						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}
		}

	}

	static class ThreadB implements Runnable {

		@Override
		public void run() {
			synchronized (ob) {
				while (temp>0) {
							ob.notify();
							temp--;
						System.out.println("线程B:" + Thread.currentThread().getName() + "获得锁,并执行:temp--" + temp );
						 
					}
				}
			}
	}
}




输出结果:

线程A获得锁:Thread-0正在执行temp--10
线程A获得锁:Thread-0正在执行temp--9
线程A获得锁:Thread-0正在执行temp--8
线程A获得锁:Thread-0正在执行temp--7
线程A获得锁:Thread-0正在执行temp--6
线程A即将等待
线程B:Thread-1获得锁,并执行:temp--4
线程B:Thread-1获得锁,并执行:temp--3
线程B:Thread-1获得锁,并执行:temp--2
线程B:Thread-1获得锁,并执行:temp--1
线程B:Thread-1获得锁,并执行:temp--0
线程A被线程B唤醒,此时temp = 0

方式三:CountDownLatch

CountDownLatch基于AQS框架,相当于维护一个线程间共享变量state

public class VolatileTest {

	private volatile static int temp = 10;

	static CountDownLatch countDownLatch  = new CountDownLatch(2);
	public static void main(String[] args) {
		Thread A = new Thread(new ThreadA());
		Thread B = new Thread(new ThreadB());
		B.start();
		A.start();
		try {
			//执行await()方法,此线程就会阻塞,直到计数器为0
			countDownLatch.await();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("主线程执行");
	}

	static class ThreadA implements Runnable {

		@Override
		public void run() {
			while (temp > 0) {
				System.out.println("线程A:" + Thread.currentThread().getName() + "正在执行");
				temp--;
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			countDownLatch.countDown();
			System.out.println("此时计数器数量为:" + countDownLatch.getCount());
		}

	}

	static class ThreadB implements Runnable {

		@Override
		public void run() {

			while (true) {
				countDownLatch.countDown(); 
					System.out.println("线程B:" + Thread.currentThread().getName() + "执行");
					System.out.println("此时计数器数量为:" + countDownLatch.getCount());
					break;
				 
			}
		}

	}
}
输出结果:

线程B:Thread-1执行
此时计数器数量为:1
线程A:Thread-0正在执行
线程A:Thread-0正在执行
线程A:Thread-0正在执行
线程A:Thread-0正在执行
线程A:Thread-0正在执行
线程A:Thread-0正在执行
线程A:Thread-0正在执行
线程A:Thread-0正在执行
线程A:Thread-0正在执行
线程A:Thread-0正在执行
此时计数器数量为:0
主线程执行

方式四 : LockSupport

备注:lockSupport不需要写在同步代码块中,实现了线程间的解锁。其次,park方法和unpark方法,没有固定顺序要求,皆可正常唤醒线程。unpark不会释放锁

public class LockSupportTest {

	public static void main(String[] args) {
		Thread threadA = new Thread(){
			public void run(){
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("当前线程A:" + Thread.currentThread().getName() + "正在执行,即将被阻塞");
				LockSupport.park();
				System.out.println("当前线程A:" + Thread.currentThread().getName() + "被唤醒");
			}
		};
		Thread threadB = new Thread(){
			public void run(){
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("当前线程B:" + Thread.currentThread().getName() + "正在执行,即将解锁A线程");
				LockSupport.unpark(threadA);
				System.out.println("当前线程B:" + Thread.currentThread().getName() + "结束");
			}
		};
		threadA.start();
		threadB.start();
	}

}

方式五 : Condition

备注:使用Condition的await()和signal()方法来代替传统的Object的wait()和notify()方法,这俩方法必须在lock保护之内。

public class ConditionTest {
	public static void main(String[] args) {
		Myservice sercive = new Myservice();
		ThreadA a = new ThreadA(sercive);
		a.setName("A");
		a.start();
		
		ThreadB b = new ThreadB(sercive);
		b.setName("B");
		b.start();
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		sercive.signalAll_A();
		
	}
	
static class ThreadA extends Thread{
	private Myservice service;
	public ThreadA(Myservice service) {
		super();
		this.service  = service;
	}
	public void run(){
		service.awaitA();
	}
}
static class ThreadB extends Thread{
	private Myservice service;
	public ThreadB(Myservice service) {
		super();
		this.service  = service;
	}
	public void run(){
		service.awaitB();
	}
}
	static class Myservice {
		private ReentrantLock lock  = new ReentrantLock();
		public Condition conditionA = lock.newCondition();
		public Condition conditionB = lock.newCondition();
		
		public void awaitA(){
			try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "线程A调用方法await前");
				conditionA.await();
				System.out.println(Thread.currentThread().getName() + "线程A调用方法await后");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				lock.unlock();
			}
		}
		public void awaitB(){
			try {
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "线程B调用方法await前");
				conditionB.await();
				System.out.println(Thread.currentThread().getName() + "线程B调用方法await后");
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally {
				lock.unlock();
			}
		}
		public void signalAll_A(){
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "线程A调用方法signalAll前");
			conditionA.signalAll();
			System.out.println(Thread.currentThread().getName() + "线程A调用方法signalAll后");
			lock.unlock();
		}
		public void signalAll_B(){
			lock.lock();
			System.out.println(Thread.currentThread().getName() + "线程B调用方法signalAll前");
			conditionB.signalAll();
			System.out.println(Thread.currentThread().getName() + "线程B调用方法signalAll后");
			lock.unlock();
		}
		
		
	}
 
}
输出结果:
A线程A调用方法await前
B线程B调用方法await前
main线程A调用方法signalAll前
main线程A调用方法signalAll后
A线程A调用方法await后

从输出的结果中分析,线程B仍处于阻塞状态没有被唤醒。Condition实现一种分组机制,对临界资源进行访问的线程进行分组,以便实现线程间更精细化的协作,例如只通知部分线程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值