当前位置:   article > 正文

线程池 及 volatile+AtomicInteger+threadLocal(原子性、可见性、有序性)_atomicinteger 和 thread

atomicinteger 和 thread

多线程有三大特性,原子性、可见性、有序性

什么是原子性

即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
一个很经典的例子就是银行账户转账问题:
比如从账户A向账户B转1000元,那么必然包括2个操作:从账户A减去1000元,往账户B加上1000元。这2个操作必须要具备原子性才能保证不出现一些意外的问题。
我们操作数据也是如此,比如i = i+1;其中就包括,读取i的值,计算i,写入i。这行代码在Java中是不具备原子性的,则多线程运行肯定会出问题,所以也需要我们使用同步和lock这些东西来确保这个特性了。
原子性其实就是保证数据一致、线程安全一部分,

什么是可见性

当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
若两个线程在不同的cpu,那么线程1改变了i的值还没刷新到主存,线程2又使用了i,那么这个i值肯定还是之前的,线程1对变量的修改线程没看到这就是可见性问题。

什么是有序性

程序执行的顺序按照代码的先后顺序执行。
一般来说处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。如下:
int a = 10; //语句1
int r = 2; //语句2
a = a + 3; //语句3
r = a*a; //语句4
则因为重排序,他还可能执行顺序为 2-1-3-4,1-3-2-4
但绝不可能 2-1-4-3,因为这打破了依赖关系。
显然重排序对单线程运行是不会有任何问题,而多线程就不一定了,所以我们在多线程编程时就得考虑这个问题了。

什么是Java内存模型:

java内存模型简称jmm,定义了一个线程对另一个线程可见。共享变量存放在主内存中,每个线程都有自己的本地内存,当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,所以就会发生线程安全问题。

join(有序性 )

使用 join 让其他线程变为等待,只有自己的线程执行完了其他线程才开始执行,


/**
 * 有序性
 * @author wangsong
 * @date:  2019年4月20日 下午5:44:11
 */

class ThreadDome extends Thread {
	@Override
	public void run() {
		System.out.println("线程一");
	}
}

class ThreadDome2 extends Thread {
	@Override
	public void run() {
		try {
			ThreadDome.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("线程二");
	}
}

class ThreadDome3 extends Thread {
	@Override
	public void run() {
		try {
			ThreadDome.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("线程三");
	}
}

public class thread_test5 {
	public static void main(String[] args) throws InterruptedException {
		
		//我们让线程执行顺序为  t3 -->  t2  --->  t1
	
		ThreadDome t1 = new ThreadDome();
		ThreadDome2 t2 = new ThreadDome2();
		ThreadDome3 t3 = new ThreadDome3();
		t3.start(); 
		t3.join();   //让其他线程等待t3线程执行完
		t2.start();  
		t2.join();   //让其他线程等待t2线程执行完
		t1.start();
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

volatile (可见性)

Volatile 关键字的作用是变量在多个线程之间可见
下代码如不指定Volatile ,线程就读取不到设置的值
线程不会关闭

package threand_dame1;

/**
 * volatile 能读取到最新的值,关键字的作用是变量在多个线程之间可见,不能保证原子性
 * @author wangsong
 * @date:  2019年4月20日 下午5:44:11
 */

class ThreadVolatileDemo extends Thread {
	
	//public  boolean flag = true;
	//不加volatile无法读到最新值,线程无法正常关闭
	public  volatile  boolean flag = true;
	
	@Override
	public void run() {
		System.out.println("开始执行子线程....");
		while (flag) {
		}
		System.out.println("线程停止");
	}
	public void setRuning(boolean flag) {
		this.flag = flag;
	}
}

public class thread_test {
	public static void main(String[] args) throws InterruptedException {
		ThreadVolatileDemo threadVolatileDemo = new ThreadVolatileDemo();
		threadVolatileDemo.start();
		Thread.sleep(3000);
		threadVolatileDemo.setRuning(false);
		System.out.println("flag 已经设置成false");
		Thread.sleep(1000);
		System.out.println(threadVolatileDemo.flag);

	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

AtomicInteger 原子性

AtomicInteger 可以保证原子性,及变量在多个线程之间可见
案列一,10线程 * 1000 因没保证读取的一致性,结果可想而知
案列二,保证了原子性,读取的一致性

package threand_dame1;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 
 * @author wangsong
 * @date:  2019年4月20日 下午5:44:11
 */

/**
 * 错误示例,发现count 10个线程 * 1000 =10000,却这么运行都到不了10000
 * 可见volatile不能保证原子性
 */

//public class thread_test2 extends Thread {
//	private static volatile int count;
//
//    //private static AtomicInteger count = new AtomicInteger(0);
//    
//	public void run() {
//		
//		for (int i = 0; i < 1000; i++) {
//			count++;
//		}
//		try {
//			thread_test2.sleep(50);
//		} catch (InterruptedException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
//		System.out.println(count);
//	
//	}
//
//	public static void main(String[] args) {
//
//		thread_test2[] arr = new thread_test2[100];
//		for (int i = 0; i < 10; i++) {
//			arr[i] = new thread_test2();
//		}
//
//		for (int i = 0; i < 10; i++) {
//			arr[i].start();
//		}
//	}
//}

/**
 *   AtomicInteger 可以保证原子性,及变量在多个线程之间可见
 * 
 */
public class thread_test2 extends Thread {
	static int count = 0;
	private static  AtomicInteger atomicInteger = new AtomicInteger(0);

	@Override
	public void run() {
			for (int i = 0; i < 1000; i++) {
				//等同于i++
				atomicInteger.incrementAndGet();
			}
			try {
				thread_test2.sleep(50);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(getName()+"---"+atomicInteger);
	}

	public static void main(String[] args) {
		// 初始化10个线程
		thread_test2[] volatileNoAtomic = new thread_test2[10];
		for (int i = 0; i < 10; i++) {
			// 创建
			volatileNoAtomic[i] = new thread_test2();
		}
		for (int i = 0; i < volatileNoAtomic.length; i++) {
			volatileNoAtomic[i].start();
		}
	}

}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86

threadLocal 独立变量

ThreadLocal提高一个线程的局部变量,访问某个线程拥有自己局部变量。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

package threand_dame1;

/**
 * ThreadLocal  让每个线程都有自己内存来处理threadLocal
 * @author wangsong
 * @date:  2019年4月20日 下午6:12:19
 */

class Res {
	// 生成序列号共享变量
	public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
		protected Integer initialValue() {
			return 0;
		};
	};

	public Integer getNum() {
		int count = threadLocal.get() + 1;
		threadLocal.set(count);
		return count;
	}
}

public class thread_test3 extends Thread {
	private Res res;

	public thread_test3(Res res) {
		this.res = res;
	}

	@Override
	public void run() {
		for (int i = 0; i < 3; i++) {
			try {
				thread_test3.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + "---" + "i---" + i + "--num:" + res.getNum());
		}
	}

	public static void main(String[] args) {
		Res res = new Res();
		thread_test3 t1 = new thread_test3(res);
		thread_test3 t2 = new thread_test3(res);
		thread_test3 t3 = new thread_test3(res);
		t1.start();
		t2.start();
		t3.start();
	}

}
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

线程池(4种)

Java通过Executors(jdk1.5并发包)提供四种线程池,分别为:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

package threand_dame1;

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

/**
 * 线程池
 * shutdownNow方法的解释是:线程池拒接收新提交的任务,同时立马关闭线程池,线程池里的任务不再执行。
 * shutdown方法的解释是:线程池拒接收新提交的任务,同时等待线程池里的任务执行完毕后关闭线程池。    
 * 
 */



public class thread_test4{

	public static void main(String[] args) {
		

		//创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		for (int i = 0; i < 10; i++) {
			final int index = i;
//			 try {
//			 Thread.sleep(index * 1000);
//			 } catch (InterruptedException e) {
//			 e.printStackTrace();
//			 }
			cachedThreadPool.execute(new Runnable() {
				public void run() {
					System.out.println(Thread.currentThread().getName() + "---" + index);
				}
			});
		}
		//关闭线程池
		cachedThreadPool.shutdown();
		
		
	
//			
//		// 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
//		final ExecutorService newCachedThreadPool = Executors.newFixedThreadPool(3);
//
//			for (int i = 0; i < 10; i++) {
//				final int index = i;
//				newCachedThreadPool.execute(new Runnable() {
//					public void run() {
//						try {
//							Thread.sleep(1000);
//						} catch (Exception e) {
//							// TODO: handle exception
//						}
//						System.out.println("i:" + index);
//					}
//				});
//			}
//			
			
			
			
//				
//		   // 创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:   TimeUnit.事件单位
//			ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);
//			newScheduledThreadPool.schedule(new Runnable() {
//				public void run() {
//					System.out.println("delay 3 seconds");
//				}
//			}, 3, TimeUnit.SECONDS);
			
			
			
			
		
//			System.out.println("====================================");
//			System.out.println("创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行");
//			System.out.println("====================================");
//			
//	     	//创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。示例代码如下:
//			ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
//			for (int i = 0; i < 10; i++) {
//				final int index = i;
//				newSingleThreadExecutor.execute(new Runnable() {
//
//					@Override
//					public void run() {
//						System.out.println("index:" + index);
//						try {
//							Thread.sleep(200);
//						} catch (Exception e) {
//							// TODO: handle exception
//						}
//					}
//				});
//			}
	}
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/294553
推荐阅读
相关标签
  

闽ICP备14008679号