java并发一文读懂(代码片段)

在奋斗的大道 在奋斗的大道     2023-03-09     804

关键词:

目录

线程实现方式

方式一:实现 Runnable 接口

方式二:实现 Callable 接口

方式三:继承 Thread 类

演示功能代码

实现接口 VS 继承 Thread

线程机制

Executor(线程执行容器)

Executor(线程执行容器)实现

CachedThreadPool 实例

FixedThreadPool实例

SingleThreadExecutor实例

演示功能代码

Daemon(守护线程)

Daemon(守护线程)实现方式

演示功能代码:

sleep(休眠)

演示功能代码:

yield(线程让步)

演示功能代码:

线程中断

InterruptedException

演示功能代码:

interrupted() 线程中断方法

演示功能代码:

Executor 中断操作

演示功能代码:

Executor 指定线程中断

演示功能代码:

线程互斥同步

synchronized

同步一个代码块

演示功能代码:

同步一个方法

同步一个类

演示功能代码:

同步一个静态方法

ReentrantLock

演示功能代码:

synchronized与ReentrantLock比较

推荐选择

线程协作

join()

演示功能代码:

wait() notify() notifyAll()

演示功能代码

wait() 和 sleep() 的区别

await() signal() signalAll()

演示功能代码

线程状态

新建(NEW)

可运行(RUNABLE)

阻塞(BLOCKED)

无限期等待(WAITING)

限期等待(TIMED_WAITING)

死亡(TERMINATED)

J.U.C - AQS

CountDownLatch

执行图

演示功能代码

CyclicBarrier

构造函数

执行图

演示功能代码

Semaphore 

演示功能代码

J.U.C - 其它组件 

FutureTask 

 演示功能代码:

BlockingQueue 

使用 BlockingQueue 实现生产者消费者模式优化

ForkJoin

演示功能代码:

线程不安全示例 

Java 内存模型

主内存与工作内存

 内存间交互操作

内存模型三大特性

原子性

 可见性

有序性

先行发生原则

单一线程原则

 管程锁定规则 

 volatile 变量规则 

线程启动规则 

线程加入规则 

 线程中断规则

对象终结规则

传递性

线程安全

不可变

演示功能代码:

 互斥同步

非阻塞同步

CAS

AtomicInteger

ABA 

无同步方案

栈封闭

 线程本地存储(Thread Local Storage) 

演示功能代码:

可重入代码(Reentrant Code)

锁优化

自旋锁

锁消除

锁粗化

轻量级锁

偏向锁

多线程开发良好的实践 


线程实现方式

方式一:实现 Runnable 接口

Runnable 接口实例

static class RunnableImpl implements Runnable
		@Override
		public void run() 
			// TODO Auto-generated method stub
			System.out.println("线程实现之Runnable 接口");
		
		
	

方式二:实现 Callable 接口

温馨提示:Callable 存在返回值,返回值通过 FutureTask 进行封装。

Callable 接口实例

	static class CallableImpl implements Callable<Boolean>

		@Override
		public Boolean call() throws Exception 
			// TODO Auto-generated method stub
			System.out.println("线程实现之Callable 接口");
			return true;
		
		
	

方式三:继承 Thread 类

Thread 类实例

/**
	 * 温馨提示:继承Thread类,覆写run方法
	 * @author zzg
	 *
	 */
	static class ThreadImpl extends Thread
		
		@Override
		public void run() 
			// TODO Auto-generated method stub
			super.run();
			System.out.println("线程实现之Thread 类");
		
	

演示功能代码

package com.zzg.test;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class RunnableTest 
	public static void main(String[] args) 
		Thread runnableImpl = new Thread(new RunnableImpl());
		runnableImpl.start();
		
		Thread threadImpl = new ThreadImpl();
		threadImpl.start();
		
		CallableImpl callableImpl = new CallableImpl();
	    FutureTask<Boolean> task = new FutureTask<>(callableImpl);
	    Thread callableImplThread = new Thread(task);
	    callableImplThread.start();
	    try 
			System.out.println("是否执行成功" + task.get());
		 catch (InterruptedException e) 
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("Callable 线程实现执行失败");
		 catch (ExecutionException e) 
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.out.println("Callable 线程实现执行失败");
		
	
	
	static class RunnableImpl implements Runnable
		@Override
		public void run() 
			// TODO Auto-generated method stub
			System.out.println("线程实现之Runnable 接口");
		
		
	
	
	/**
	 * 温馨提示:继承Thread类,覆写run方法
	 * @author zzg
	 *
	 */
	static class ThreadImpl extends Thread
		
		@Override
		public void run() 
			// TODO Auto-generated method stub
			super.run();
			System.out.println("线程实现之Thread 类");
		
	
	
	static class CallableImpl implements Callable<Boolean>

		@Override
		public Boolean call() throws Exception 
			// TODO Auto-generated method stub
			System.out.println("线程实现之Callable 接口");
			return true;
		
		
	


实现接口 VS 继承 Thread

实现接口会更好一些,因为:

  • Java 不支持多重继承,因此继承了 Thread 类就无法继承其它类,但是可以实现多个接口;
  • 类可能只要求可执行就行,继承整个 Thread 类开销过大。

线程机制

Executor(线程执行容器)

Executor 管理多个异步任务的执行,而无需程序员显式地管理线程的生命周期。这里的异步是指多个任务的执行互不干扰,不需要进行同步操作

Executor(线程执行容器)实现

主要有三种 Executor:

  • CachedThreadPool:一个任务创建一个线程;
  • FixedThreadPool:所有任务只能使用固定大小的线程;
  • SingleThreadExecutor:相当于大小为 1 的 FixedThreadPool。

CachedThreadPool 实例

/**
	 * CachedThreadPool 线程池调用
	 */
	public static void cachedThreadPoolTest()
		 ExecutorService executorService = Executors.newCachedThreadPool();
		    for (int i = 0; i < 5; i++) 
		        executorService.execute(new RunnableImpl("CachedThreadPool"));
		    
		    executorService.shutdown();
	

FixedThreadPool实例

/**
	 * FixedThreadPool 线程池调用
	 */
	public static void fixedThreadPoolTest()
		 ExecutorService executorService = Executors.newFixedThreadPool(5);
		    for (int i = 0; i < 5; i++) 
		        executorService.execute(new RunnableImpl("FixedThreadPool"));
		    
		    executorService.shutdown();
	

SingleThreadExecutor实例

/**
	 * SingleThreadExecutor 线程池调用
	 */
	public static void singleThreadExecutorTest()
		 ExecutorService executorService = Executors.newSingleThreadExecutor();
		    for (int i = 0; i < 5; i++) 
		        executorService.execute(new RunnableImpl("SingleThreadExecutor"));
		    
		    executorService.shutdown();
	

演示功能代码

package com.zzg.test;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorTest 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		cachedThreadPoolTest();
		fixedThreadPoolTest();
		singleThreadExecutorTest();
	
	
	/**
	 * CachedThreadPool 线程池调用
	 */
	public static void cachedThreadPoolTest()
		 ExecutorService executorService = Executors.newCachedThreadPool();
		    for (int i = 0; i < 5; i++) 
		        executorService.execute(new RunnableImpl("CachedThreadPool"));
		    
		    executorService.shutdown();
	
	
	/**
	 * FixedThreadPool 线程池调用
	 */
	public static void fixedThreadPoolTest()
		 ExecutorService executorService = Executors.newFixedThreadPool(5);
		    for (int i = 0; i < 5; i++) 
		        executorService.execute(new RunnableImpl("FixedThreadPool"));
		    
		    executorService.shutdown();
	
	
	/**
	 * SingleThreadExecutor 线程池调用
	 */
	public static void singleThreadExecutorTest()
		 ExecutorService executorService = Executors.newSingleThreadExecutor();
		    for (int i = 0; i < 5; i++) 
		        executorService.execute(new RunnableImpl("SingleThreadExecutor"));
		    
		    executorService.shutdown();
	
	
	static class RunnableImpl implements Runnable
		private String name;
		
		public String getName() 
			return name;
		
		
		public void setName(String name) 
			this.name = name;
		
		
		public RunnableImpl(String name) 
			super();
			this.name = name;
		

		@Override
		public void run() 
			// TODO Auto-generated method stub
			System.out.println("线程执行容器之"+this.name+"线程实现Runnabe接口");
		
		
	


Daemon(守护线程)

守护线程是程序运行时在后台提供服务的线程,不属于程序中不可或缺的部分。

温馨提示

  • 当所有非守护线程结束时,程序也就终止,同时会杀死所有守护线程。
  • main() 属于非守护线程。

Daemon(守护线程)实现方式

在线程启动之前使用 setDaemon() 方法可以将一个线程设置为守护线程。

演示功能代码:

package com.zzg.test;

public class DaemonTest 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		Thread thread = new Thread(new RunnableImpl());
		// 设置thread 线程为守护线程
		thread.setDaemon(true);
		// 守护线程启动
		thread.start();
	
	
	
	static class RunnableImpl implements Runnable

		@Override
		public void run() 
			// TODO Auto-generated method stub
			System.out.println("线程实现之Runnable 接口");
		
		
	


sleep(休眠)

Thread.sleep(millisec) 方法会休眠当前正在执行的线程,millisec 单位为毫秒。

重点注意:sleep() 可能会抛出 InterruptedException,因为异常不能跨线程传播回 main() 中,因此必须在本地进行处理。线程中抛出的其它异常也同样需要在本地进行处理。

演示功能代码:

package com.zzg.test;

import java.text.SimpleDateFormat;
import java.util.Date;

public class SleepTest 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		Thread thread = new Thread(new RunnableImpl());
		// 线程启动
		thread.start();	
	
	
	static class RunnableImpl implements Runnable

		@Override
		public void run() 
			// TODO Auto-generated method stub
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
			System.out.println("线程开始执行时间" + format.format(new Date()));
			try 
				Thread.sleep(3000);
			 catch (InterruptedException e) 
				// TODO Auto-generated catch block
				e.printStackTrace();
			
			System.out.println("线程开始结束时间" + format.format(new Date()));
		
		
	


yield(线程让步)

Thread.yield() 的调用声明了当前线程已经完成了生命周期中最重要的部分,可以切换给其它线程来执行。该方法只是对线程调度器的一个建议,而且也只是建议具有相同优先级的其它线程可以运行。

演示功能代码:

package com.zzg.test;

import java.text.SimpleDateFormat;
import java.util.Date;

import com.digipower.test.SleepTest.RunnableImpl;

public class YieldTest 
	
	public static void main(String[] args) 
		Thread thread = new Thread(new RunnableImpl("normal Runnable"));
		Thread yieLdThread = new Thread(new YieldRunnableImpl("yidld Runnable"));
		// 设置线程等级
		//yieLdThread.setPriority(Thread.MAX_PRIORITY);
		// 线程启动
		yieLdThread.start();
		thread.start();	
	
	
	static class RunnableImpl implements Runnable
		
		private String name;
		
		public String getName() 
			return name;
		

		public void setName(String name) 
			this.name = name;
		

		public RunnableImpl(String name) 
			super();
			this.name = name;
		

		@Override
		public void run() 
			// TODO Auto-generated method stub
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
			System.out.println(this.name + ",线程执行时间" + format.format(new Date()));
			
			try 
				Thread.sleep(3000);
			 catch (InterruptedException e) 
				// TODO Auto-generated catch block
				e.printStackTrace();
			
			
			System.out.println(this.name + ",线程结束时间" + format.format(new Date()));
		
		
	
	
	static class YieldRunnableImpl implements Runnable
		
		private String name;
		
		public String getName() 
			return name;
		

		public void setName(String name) 
			this.name = name;
		

		public YieldRunnableImpl(String name) 
			super();
			this.name = name;
		

		@Override
		public void run() 
			// TODO Auto-generated method stub
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
			System.out.println(this.name + ",线程执行时间" + format.format(new Date()));
			// 线程让步
			Thread.yield();
			
			try 
				Thread.sleep(3000);
			 catch (InterruptedException e) 
				// TODO Auto-generated catch block
				e.printStackTrace();
			
			
			System.out.println(this.name + ",线程结束时间" + format.format(new Date()));
		
		
	
	
	


线程中断

一个线程执行完毕之后会自动结束,如果在运行过程中发生异常也会提前结束。

InterruptedException

通过调用一个线程的 interrupt() 来中断该线程,如果该线程处于阻塞、限期等待或者无限期等待状态,那么就会抛出 InterruptedException,从而提前结束该线程。但是不能中断 I/O 阻塞和 synchronized 锁阻塞。

演示功能代码:

代码片段说明:在 main() 中启动一个线程之后再中断它,由于线程中调用了 Thread.sleep() 方法,因此会抛出一个 InterruptedException,从而提前结束线程,不执行之后的语句。

package com.zzg.test;

import java.text.SimpleDateFormat;
import java.util.Date;

public class InterruptedExceptionTest 

	public static void main(String[] args) 
		Thread thread = new Thread(new RunnableImpl("interrupted Runnable"));
		thread.start();
		// 调用线程中断方法
		thread.interrupt();
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		System.out.println("Main run" + format.format(new Date()));

	

	static class RunnableImpl implements Runnable 

		private String name;

		public String getName() 
			return name;
		

		public void setName(String name) 
			this.name = name;
		

		public RunnableImpl(String name) 
			super();
			this.name = name;
		

		@Override
		public void run() 
			// TODO Auto-generated method stub
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
			System.out.println(this.name + ",线程执行时间" + format.format(new Date()));

			try 
				Thread.sleep(3000);
			 catch (InterruptedException e) 
				// TODO Auto-generated catch block
				e.printStackTrace();
				// 终止线程继续运行
				return;
			

			System.out.println(this.name + ",线程结束时间" + format.format(new Date()));
		

	

运行结果:

interrupted Runnable,线程执行时间2022-03-18 05:29:05
Main run2022-03-18 05:29:05
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at com.digipower.test.InterruptedExceptionTest$RunnableImpl.run(InterruptedExceptionTest.java:42)
	at java.lang.Thread.run(Thread.java:745)

interrupted() 线程中断方法

问题描述:如果一个线程的 run() 方法执行一个无限循环,并且没有执行 sleep() 等会抛出 InterruptedException 的操作,那么调用线程的 interrupt() 方法就无法使线程提前结束。

问题解决办法:在线程中设置中断标记,此时调用 interrupted() 方法会返回 true。因此可以在循环体中使用 interrupted() 方法来判断线程是否处于中断状态,从而提前结束线程。

演示功能代码:

package com.zzg.test;

import java.text.SimpleDateFormat;
import java.util.Date;

public class InterruptedTest 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		Thread thread = new ThreadImpl("Interrupted  Runnabel");
		thread.start();
		
		// 主线程休眠
		try 
			Thread.sleep(500);
		 catch (InterruptedException e) 
			// TODO Auto-generated catch block
			e.printStackTrace();
		
		// 调用线程的中断方法
		thread.interrupt();
		// 主线程执行完毕
		System.out.println("Main Run");
	
	
	static class ThreadImpl extends Thread 

		private String threadNam;

		
		public String getThreadNam() 
			return threadNam;
		

		public void setThreadNam(String threadNam) 
			this.threadNam = threadNam;
		

		public ThreadImpl(String threadNam) 
			super();
			this.threadNam = threadNam;
		

		@Override
		public void run() 
			// TODO Auto-generated method stub
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
			System.out.println(this.threadNam + ",线程执行时间" + format.format(new Date()));
			
			 while (!interrupted()) 
				 System.out.println("我没有收到中断信息"); 
	         

			
			System.out.println("我进入了中断线程的条件中,将要结束run方法"); 
			System.out.println(this.threadNam + ",线程结束时间" + format.format(new Date()));
		

	


Executor 中断操作

调用 Executor 的 shutdown() 方法会等待线程都执行完毕之后再关闭,但是如果调用的是 shutdownNow() 方法,则相当于调用每个线程的 interrupt() 方法。

演示功能代码:

package com.zzg.test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExecutorInterruptTest 
	
	public static void cachedThreadPoolTest()
		 ExecutorService executorService = Executors.newCachedThreadPool();
		    for (int i = 0; i < 5; i++) 
		        executorService.execute(new RunnableImpl("CachedThreadPool"));
		    
		   // 立即中断线程池中的线程
		    executorService.shutdownNow();
		   // 线程池中的线程全部执行完毕后,执行线程池关闭操作。
		   // executorService.shutdown();
	

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		cachedThreadPoolTest();
	
	
	
	static class RunnableImpl implements Runnable
		private String name;
		
		public String getName() 
			return name;
		
		
		public void setName(String name) 
			this.name = name;
		
		
		public RunnableImpl(String name) 
			super();
			this.name = name;
		

		@Override
		public void run() 
			// TODO Auto-generated method stub
			System.out.println("线程执行容器之"+this.name+"线程实现Runnabe接口");
			try 
				Thread.sleep(3000);
			 catch (InterruptedException e) 
				// TODO Auto-generated catch block
				e.printStackTrace();
				System.out.println("线程执行中断操作");
				return;
			
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			System.out.println("线程正常执行完毕" + format.format(new Date()));
		
		
	


Executor 指定线程中断

如果只想中断 Executor 中的一个线程,可以通过使用 submit() 方法来提交一个线程,它会返回一个 Future<?> 对象,通过调用该对象的 cancel(true) 方法就可以中断线程。

演示功能代码:

package com.zzg.test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ExecutorInterruptTest 
	
	public static void cachedThreadPoolTest()
		 ExecutorService executorService = Executors.newCachedThreadPool();
		    for (int i = 0; i < 5; i++) 
		        executorService.execute(new RunnableImpl("CachedThreadPool"));
		    
		   // 立即中断线程池中的线程
		    executorService.shutdownNow();
		   // 线程池中的线程全部执行完毕后,执行线程池关闭操作。
		   // executorService.shutdown();
	
	
	public static void cachedThreadPoolTestInterrupt()
		 ExecutorService executorService = Executors.newCachedThreadPool();
		    for (int i = 0; i < 5; i++) 
		    	Future<?> future = executorService.submit(new RunnableImpl("CachedThreadPool" + i));
		    	if(i == 3)
		    		// 中断满足条件的线程
		    		future.cancel(true);
		    	
		    
		   // 立即中断线程池中的线程
		   // executorService.shutdownNow();
		   // 线程池中的线程全部执行完毕后,执行线程池关闭操作。
		   executorService.shutdown();
	

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		// cachedThreadPoolTest();
		cachedThreadPoolTestInterrupt();
	
	
	
	static class RunnableImpl implements Runnable
		private String name;
		
		public String getName() 
			return name;
		
		
		public void setName(String name) 
			this.name = name;
		
		
		public RunnableImpl(String name) 
			super();
			this.name = name;
		

		@Override
		public void run() 
			// TODO Auto-generated method stub
			System.out.println("线程执行容器之"+this.name+"线程实现Runnabe接口");
			try 
				Thread.sleep(3000);
			 catch (InterruptedException e) 
				// TODO Auto-generated catch block
				e.printStackTrace();
				System.out.println("线程执行中断操作");
				return;
			
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			System.out.println("线程正常执行完毕" + format.format(new Date()));
		
		
	


线程互斥同步

Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized,而另一个是 JDK 实现的 ReentrantLock。

synchronized

同步一个代码块

语法:

public void func() 
    synchronized (this) 
        // ...
    

功能说明:它只作用于同一个对象,如果调用两个对象上的同步代码块,就不会进行同步。

演示功能代码:

package com.zzg.test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SynchronizedBlockTest 
	
	public static void cachedThreadPoolTest()
		 ExecutorService executorService = Executors.newCachedThreadPool();
		 
		 SynchronizedObject object = new SynchronizedObject();
		    for (int i = 0; i < 5; i++) 
		        executorService.execute(new Synchronizedhread(object, "SynchronizedBlockThread" + i));
		    
		 
		   // 线程池中的线程全部执行完毕后,执行线程池关闭操作。
		   executorService.shutdown();
	

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		cachedThreadPoolTest();
	
	
	static class SynchronizedObject
		public void printNUmber()
			synchronized (this) 
				for (int i = 0; i < 20; i++) 
					String name = Thread.currentThread().getName();
	                System.out.println(name +"正在输出数字" + i);
	                // 当前正在执行的线程进行休眠,锁不会释放
	                try 
						Thread.sleep(1000);
					 catch (InterruptedException e) 
						// TODO Auto-generated catch block
						e.printStackTrace();
						// 出现异常,立即中止当前循环
						break;
					
	                System.out.println(name +"数字输出完毕" );
	            
			
		
	
	
	static class Synchronizedhread extends Thread
		private SynchronizedObject object;
		
		private String threadName;
		
		public String getThreadName() 
			return threadName;
		

		public void setThreadName(String threadName) 
			this.threadName = threadName;
		

		public SynchronizedObject getObject() 
			return object;
		

		public void setObject(SynchronizedObject object) 
			this.object = object;
		

		public Synchronizedhread(SynchronizedObject object, String threadName) 
			super();
			this.object = object;
			this.setName(threadName);
		

		@Override
		public void run() 
			// TODO Auto-generated method stub
			super.run();
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			System.out.println(this.getName() + "线程:开始启动" + format.format(new Date()));
			// 执行同步对象方法
			this.object.printNUmber();
			System.out.println(this.getName()  + "线程:运行结束" + format.format(new Date()));
		
		
		
		
	


同步一个方法

语法:

public synchronized void func () 
    // ...

功能说明:它和同步代码块一样,作用于同一个对象。

同步一个类

语法:

public void func() 
    synchronized (SynchronizedExample.class) 
        // ...
    

功能说明:作用于整个类,也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步。

演示功能代码:

package com.digipower.test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class SynchronizedClassTest 
	public static void cachedThreadPoolTest()
		 ExecutorService executorService = Executors.newCachedThreadPool();
		 
		
		    for (int i = 0; i < 5; i++) 
		    	SynchronizedObject object = new SynchronizedObject();
		        executorService.execute(new Synchronizedhread(object, "SynchronizedBlockThread" + i));
		    
		 
		   // 线程池中的线程全部执行完毕后,执行线程池关闭操作。
		   executorService.shutdown();
	

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		cachedThreadPoolTest();
	
	
	static class SynchronizedObject
		public void printNUmber()
			synchronized (SynchronizedObject.class) 
				for (int i = 0; i < 10; i++) 
					String name = Thread.currentThread().getName();
	                System.out.println(name +"正在输出数字" + i);
	                // 当前正在执行的线程进行休眠,锁不会释放
	                try 
						Thread.sleep(1000);
					 catch (InterruptedException e) 
						// TODO Auto-generated catch block
						e.printStackTrace();
						// 出现异常,立即中止当前循环
						break;
					
	                System.out.println(name +"数字输出完毕" );
	            
			
		
	
	
	static class Synchronizedhread extends Thread
		private SynchronizedObject object;
		
		private String threadName;
		
		public String getThreadName() 
			return threadName;
		

		public void setThreadName(String threadName) 
			this.threadName = threadName;
		

		public SynchronizedObject getObject() 
			return object;
		

		public void setObject(SynchronizedObject object) 
			this.object = object;
		

		public Synchronizedhread(SynchronizedObject object, String threadName) 
			super();
			this.object = object;
			this.setName(threadName);
		

		@Override
		public void run() 
			// TODO Auto-generated method stub
			super.run();
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			System.out.println(this.getName() + "线程:开始启动" + format.format(new Date()));
			// 执行同步对象方法
			this.object.printNUmber();
			System.out.println(this.getName()  + "线程:运行结束" + format.format(new Date()));
		
		
		
		
	

同步一个静态方法

语法:

public synchronized static void fun() 
    // ...

功能说明:作用于整个类。

ReentrantLock

ReentrantLock 是 java.util.concurrent(J.U.C)包中的锁。

演示功能代码:

package com.zzg.test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest 
	
	public static void cachedThreadPoolTest()
		 ExecutorService executorService = Executors.newCachedThreadPool();
		 
		
		    for (int i = 0; i < 5; i++) 
		    	ReentrantLockObject object = new ReentrantLockObject();
		        executorService.execute(new Synchronizedhread(object,  "" + i));
		    
		 
		   // 线程池中的线程全部执行完毕后,执行线程池关闭操作。
		   executorService.shutdown();
	

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		cachedThreadPoolTest();
	

	static class ReentrantLockObject 
		private Lock lock = new ReentrantLock();

		public void func() 
			lock.lock();
			try 
				for (int i = 0; i < 10; i++) 
					String name = Thread.currentThread().getName();
	                System.out.println(name +"正在输出数字" + i);
	                System.out.println(name +"数字输出完毕" );
				
			 finally 
				lock.unlock(); // 确保释放锁,从而避免发生死锁。
			
		
	
	
	static class Synchronizedhread extends Thread
		private ReentrantLockObject object;
		
		private String threadName;
		
		public String getThreadName() 
			return threadName;
		

		public void setThreadName(String threadName) 
			this.threadName = threadName;
		

		public ReentrantLockObject getObject() 
			return object;
		

		public void setObject(ReentrantLockObject object) 
			this.object = object;
		

		public Synchronizedhread(ReentrantLockObject object, String threadName) 
			super();
			this.object = object;
			this.setName("ReentrantLockThread"+threadName);
		

		@Override
		public void run() 
			// TODO Auto-generated method stub
			super.run();
			SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			System.out.println(this.getName() + "线程:开始启动" + format.format(new Date()));
			// 执行同步对象方法
			this.object.func();
			System.out.println(this.getName()  + "线程:运行结束" + format.format(new Date()));
		
		
		
		
	


synchronized与ReentrantLock比较

1. 锁的实现

synchronized 是 JVM 实现的,而 ReentrantLock 是 JDK 实现的。

2. 性能

新版本 Java 对 synchronized 进行了很多优化,例如自旋锁等,synchronized 与 ReentrantLock 大致相同。

3. 等待可中断

当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。

ReentrantLock 可中断,而 synchronized 不行。

4. 公平锁

公平锁是指多个线程在等待同一个锁时,必须按照申请锁的时间顺序来依次获得锁。

synchronized 中的锁是非公平的,ReentrantLock 默认情况下也是非公平的,但是也可以是公平的。

5. 锁绑定多个条件

一个 ReentrantLock 可以同时绑定多个 Condition 对象。

推荐选择

除非需要使用 ReentrantLock 的高级功能,否则优先使用 synchronized。这是因为 synchronized 是 JVM 实现的一种锁机制,JVM 原生地支持它,而 ReentrantLock 不是所有的 JDK 版本都支持。并且使用 synchronized 不用担心没有释放锁而导致死锁问题,因为 JVM 会确保锁的释放。

线程协作

当多个线程可以一起工作去解决某个问题时,如果某些部分必须在其它部分之前完成,那么就需要对线程进行协调。

join()

在线程中调用另一个线程的 join() 方法,会将当前线程挂起,而不是忙等待,直到目标线程结束。自身才会再次执行。

演示功能代码:

演示功能说明:b 线程优先启动,但是因为在 b 线程中调用了 a 线程的 join() 方法,b 线程会等待 a 线程结束才继续执行,因此最后能够保证 a 线程的输出先于 b 线程的输出。

package com.zzg.test;

public class ThreadJoinTest 

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		ThreadA a = new ThreadA();
		ThreadB b = new ThreadB(a);
		// 设置线程B,执行级别最高
		b.setPriority(Thread.MAX_PRIORITY);
		a.start();
		b.start();
	

	static class ThreadA extends Thread 

		@Override
		public void run() 
			// TODO Auto-generated method stub
			super.run();
			System.out.println("线程A开始执行");
			try 
				Thread.sleep(3000);
			 catch (InterruptedException e) 
				// TODO Auto-generated catch block
				e.printStackTrace();
				// 线程出现异常,终止代码继续执行
				System.out.println("线程A出现中断异常");
				return;
			
			System.out.println("线程A执行完毕");
		

	

	static class ThreadB extends Thread 
		private ThreadA a;

		public ThreadA getA() 
			return a;
		

		public void setA(ThreadA a) 
			this.a = a;
		

		public ThreadB(ThreadA a) 
			super();
			this.a = a;
		

		@Override
		public void run() 
			// TODO Auto-generated method stub
			super.run();
			System.out.println("线程B开始执行");
			try 
				a.join();
			 catch (InterruptedException e) 
				e.printStackTrace();
				// 线程异常中断,终止代码继续执行。
				System.out.println("线程B出现中断异常");
				return;
			
			System.out.println("线程B执行完毕");
		

	


wait() notify() notifyAll()

调用 wait() 使得线程等待某个条件满足,线程在等待时会被挂起,当其他线程的运行使得这个条件满足时,其它线程会调用 notify() 或者 notifyAll() 来唤醒挂起的线程。

它们都属于 Object 的一部分,而不属于 Thread。

只能用在同步方法或者同步控制块中使用,否则会在运行时抛出 IllegalMonitorStateException。

使用 wait() 挂起期间,线程会释放锁。这是因为,如果没有释放锁,那么其它线程就无法进入对象的同步方法或者同步控制块中,那么就无法执行 notify() 或者 notifyAll() 来唤醒挂起的线程,造成死锁。

演示功能代码

package com.zzg.test;

/**
 * 多线程模式之生产与消费者模式
 * 
 * @author zzg
 *
 */
public class WaitNotifyTest 

	private static Integer count = 0;
	private static final Integer FULL = 5;
	private static Object lock = "lock";

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		Thread product = new Thread(new Product());
		Thread comsumer = new Thread(new Consumer());
		
		product.start();
		comsumer.start();
		
	
	
	static class Product implements Runnable

		@Override
		public void run() 
			// TODO Auto-generated method stub
			 for (int i = 0; i < 5; i++)
	         
	             try 
	                 Thread.sleep(1000);
	              catch (InterruptedException e1) 
	                 // TODO Auto-generated catch block
	                 e1.printStackTrace();
	                 System.out.println("生成线程在A代码段出现中断异常");
	             
	             synchronized (lock)
	             
	            	 System.out.println("生产count 属性值:" + count);
	                 while (count == FULL)
	                 
	                         try 
	                        	 System.out.println("生产线程释放锁");
	                             lock.wait();
	                          catch (InterruptedException e) 
	                             // TODO Auto-generated catch block
	                             e.printStackTrace();
	                             System.out.println("生成线程在B代码段出现中断异常");
	                         
	                 
	                 count++;
	                 System.out.println(Thread.currentThread().getName() + "produce:: " + count);
	                 lock.notifyAll();
	                 System.out.println("生产线程唤醒消费线程");
	             
	         
		
		
	
	
	static class Consumer implements Runnable

		@Override
		public void run() 
			for(int i =0; i < 5; i++)
				// 当前线程执行休眠
				try 
					Thread.sleep(1000);
				 catch (InterruptedException e) 
					// TODO Auto-generated catch block
					e.printStackTrace();
					System.out.println("当前消费者线程在A代码段出现中断异常");
				
				// 线程公用lock对象
				synchronized (lock) 
					// 判断lock对象 调用wait 和notifyAll 方法判断条件
					while(count ==0)
						try 
							System.out.println("消费者线程释放锁");
							lock.wait();
						 catch (InterruptedException e) 
							// TODO Auto-generated catch block
							e.printStackTrace();
							System.out.println("当前消费者线程在B代码段出现中断异常");
						
					
					 count--;
	                 System.out.println(Thread.currentThread().getName()+ "consume:: " + count);
	                 lock.notifyAll();
	                 System.out.println("消费者线程唤醒生产者线程");
				
				
			
			
		
		
	


wait() 和 sleep() 的区别

  • wait() 是 Object 的方法,而 sleep() 是 Thread 的静态方法;
  • wait() 会释放锁,sleep() 不会。

await() signal() signalAll()

java.util.concurrent 类库中提供了 Condition 类来实现线程之间的协调,可以在 Condition 上调用 await() 方法使线程等待,其它线程调用 signal() 或 signalAll() 方法唤醒等待的线程。

相比于 wait() 这种等待方式,await() 可以指定等待的条件,因此更加灵活。

演示功能代码

package com.zzg.test;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 多线程模式之生产与消费者模式
 * 
 * @author zzg
 *
 */
public class AwaitSignalTest 
	
	private static Lock lock = new ReentrantLock();
    private static Condition condition = lock.newCondition();
    
	private static Integer count = 0;
	private static final Integer FULL = 5;

	public static void main(String[] args) 
		// TODO Auto-generated method stub
		Thread product = new Thread(new Product());
		Thread comsumer = new Thread(new Consumer());
		
		product.start();
		comsumer.start();
	
	
	static class Product implements Runnable

		@Override
		public void run() 
			// TODO Auto-generated method stub
			 for (int i = 0; i < 5; i++)
	         
	             try 
	                 Thread.sleep(1000);
	              catch (InterruptedException e1) 
	                 // TODO Auto-generated catch block
	                 e1.printStackTrace();
	                 System.out.println("生成线程在A代码段出现中断异常");
	             
	             lock.lock();
	             try
	            	 System.out.println("生产count 属性值:" + count);
	                 while (count == FULL)
	                 
	                         try 
	                        	 System.out.println("生产线程释放锁");
	                        	 condition.await();
	                          catch (InterruptedException e) 
	                             // TODO Auto-generated catch block
	                             e.printStackTrace();
	                             System.out.println("生成线程在B代码段出现中断异常");
	                         
	                 
	                 count++;
	                 System.out.println(Thread.currentThread().getName() + "produce:: " + count);
	                 condition.signalAll();
	                 System.out.println("生产线程唤醒消费线程");
	             finally 
	            	 lock.unlock();
	             
	         
		
		
	
	
	static class Consumer implements Runnable

		@Override
		public void run() 
			for(int i =0; i < 5; i++)
				// 当前线程执行休眠
				try 
					Thread.sleep(1000);
				 catch (InterruptedException e) 
					// TODO Auto-generated catch block
					e.printStackTrace();
					System.out.println("当前消费者线程在A代码段出现中断异常");
				
				// 线程公用lock对象
				 lock.lock(); 
				 try
					// 判断lock对象 调用wait 和notifyAll 方法判断条件
					while(count ==0)
						try 
							System.out.println("消费者线程释放锁");
							condition.await();
						 catch (InterruptedException e) 
							// TODO Auto-generated catch block
							e.printStackTrace();
							System.out.println("当前消费者线程在B代码段出现中断异常");
						
					
					 count--;
	                 System.out.println(Thread.currentThread().getName()+ "consume:: " + count);
	                 condition.signalAll();
	                 System.out.println("消费者线程唤醒生产者线程");
				finally
					lock.unlock();
				
				
			
			
		
		
	


线程状态

一个线程只能处于一种状态,并且这里的线程状态特指 Java 虚拟机的线程状态,不能反映线程在特定操作系统下的状态。

新建(NEW)

创建后尚未启动。

可运行(RUNABLE)

正在 Java 虚拟机中运行。但是在操作系统层面,它可能处于运行状态,也可能等待资源调度(例如处理器资源),资源调度完成就进入运行状态。所以该状态的可运行是指可以被运行,具体有没有运行要看底层操作系统的资源调度。

阻塞(BLOCKED)

请求获取 monitor lock 从而进入 synchronized 函数或者代码块,但是其它线程已经占用了该 monitor lock,所以出于阻塞状态。要结束该状态进入从而 RUNABLE 需要其他线程释放 monitor lock。

无限期等待(WAITING)

等待其它线程显式地唤醒。

阻塞和等待的区别在于,阻塞是被动的,它是在等待获取 monitor lock。而等待是主动的,通过调用 Object.wait() 等方法进入。

一文读懂springbean的生命周期(代码片段)

...端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。一、前言今天我们来说一说SpringBean的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象。因为SpringBean的生命周期是除... 查看详情

一文读懂springbean的生命周期(代码片段)

...端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。一、前言今天我们来说一说SpringBean的生命周期,小伙伴们应该在面试中经常遇到,这是正常现象。因为SpringBean的生命周期是除... 查看详情

java并发关键字大练兵—一文读懂各个关键字(代码片段)

本文介绍了Threadlocal、volatile、condition、Semaphore、CountDownLatch、unsafe等关键字目录如下:Threadlocal本地线程volatileconditionCountDownLatch闩锁CyclicBarrier篱栅Semaphore信号灯unsafe魔法类StampedLock新读写锁1.Threadlocal从名字我们就可以看到Thr... 查看详情

一文读懂java中的代理模式(代码片段)

代理(Proxy)模式是我们在工作中广泛使用的设计模式之一,提供了对目标对象额外的访问方式。通过代理对象来访问目标对象,可以对目标对象进行功能的增强,即扩展目标对象的功能。例如在Spring中,AOP就是使用动态代理来... 查看详情

一文读懂物联网mqtt协议之基础特性篇(代码片段)

...端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。一、前言上个月有个读者问我物联网MQTT协议实战相关的问题,我说后面会搞,没想到不知不觉一个月了,太忙了,再怎... 查看详情

一文读懂物联网mqtt协议之基础特性篇(代码片段)

...端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。一、前言上个月有个读者问我物联网MQTT协议实战相关的问题,我说后面会搞,没想到不知不觉一个月了,太忙了,再怎... 查看详情

任务调度框架quartz一文读懂(代码片段)

1、Quartz简介Quartz是OpenSymphony开源组织在Jobscheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer,Quartz增加了很多功能:持久性作业-就是保持调度定时的... 查看详情

任务调度框架quartz一文读懂(代码片段)

1、Quartz简介Quartz是OpenSymphony开源组织在Jobscheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer,Quartz增加了很多功能:持久性作业-就是保持调度定时的... 查看详情

一文彻底读懂python装饰器(代码片段)

装饰器主要用途是:不修改函数源码的前提下,添加额外的功能。如果你有Java开发经验,你会发现,Python中的装饰器其实就类似于Java的注解。好的,废话不多说,进入正题。我们假想如下一个场景:... 查看详情

一文彻底读懂python装饰器(代码片段)

装饰器主要用途是:不修改函数源码的前提下,添加额外的功能。如果你有Java开发经验,你会发现,Python中的装饰器其实就类似于Java的注解。好的,废话不多说,进入正题。我们假想如下一个场景:... 查看详情

elasticsearch一文读懂(代码片段)

目录1、Elasticsearch简介2、Docker安装 Elasticsearch2.1使用Docker安装ElasticSearch7.6.22.2Elasticsearch目录详解2.3 使用Docker安装elasticSearch--head通过Chrome插件安装ElasticSearch-head  2.4了解ELKELK功能结构图Docker安装Kibana 3ElasticSe 查看详情

modelmapper一文读懂(代码片段)

目录1、ModelMapper简介1.1引入ModelMapper的依赖 1.2进行Mapping映射1.3ModelMapper工作原理 2、ModelMapper基础操作 2.1ModelMapper基本映射2.2  ModelMapper集合转换 2.3  ModelMapper指定默认值 2.4  ModelMapper属性值转换 2.5  ModelMapper属性值跳过 ... 查看详情

夯实java基础系列22:一文读懂java序列化和反序列化(代码片段)

本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看https://github.com/h2pl/Java-Tutorial喜欢的话麻烦点下Star哈文章首发于我的个人博客:www.how2playlife.com本文参考http://www.importnew.com/17964.html和https... 查看详情

夯实java基础系列23:一文读懂继承封装多态的底层实现原理(代码片段)

本系列文章将整理到我在GitHub上的《Java面试指南》仓库,更多精彩内容请到我的仓库里查看https://github.com/h2pl/Java-Tutorial喜欢的话麻烦点下Star哈文章首发于我的个人博客:www.how2playlife.com从JVM结构开始谈多态Java对于方法调用动态... 查看详情

fastdfs一文读懂(代码片段)

目录FastDFS介绍FastDFS概念FastDFS作用FastDFS优缺点 FastDFS相关概念FastDFS原理FastDFS系统拓扑图FastDFS核心工作流程FastDFS文件上传FastDFS文件下载 Linux环境搭建FastDFSDocker环境搭建FastDFSSpringBoot封装FastDFS功能模块FastDFS介绍FastDFS概念FastDFS... 查看详情

[转帖]一文读懂http/2(代码片段)

一文读懂HTTP/2http://support.upyun.com/hc/kb/article/1048799/又小拍 ? 发表于:2017年05月18日15:34:45 ? 更新于:2017年05月24日15:06:11今天,HTTP1.1已经变成互联网中主要的协议。但是在HTTP协议诞生初期却被认为是简单直接的协议... 查看详情

mapstruct一文读懂(代码片段)

目录1、MapStruct简介1.1MapStructMaven引入 2、MapStruct基础操作 2.1MapStruct基本映射 2.2MapStruct指定默认值2.3MapStruct表达式2.4MapStruct时间格式2.5MapStruct数字格式3、MapStruct组合映射3.1多参数源映射3.2使用其他参数值 3.3嵌套映射  3.4 逆... 查看详情

一文读懂javagc原理和调优(代码片段)

概述本文介绍GC基础原理和理论,GC调优方法思路和方法,基于Hotspotjdk1.8,学习之后将了解如何对生产系统出现的GC问题进行排查解决阅读时长约30分钟,内容主要如下:GC基础原理,涉及调优目标,GC事件分类、JVM内存分配策略... 查看详情

免责声明:如内容涉及版权或违规等问题,请在尽快内联系我们pp114#vip.qq.com,我们将在第一时间删除内容。

进入方法退出方法