线程池 及 volatile+AtomicInteger+threadLocal(原子性、可见性、有序性) 您所在的位置:网站首页 threadlocal如何保证线程同步 线程池 及 volatile+AtomicInteger+threadLocal(原子性、可见性、有序性)

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

2024-07-15 13:04| 来源: 网络整理| 查看: 265

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

什么是原子性

即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 一个很经典的例子就是银行账户转账问题: 比如从账户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(); } } 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); } } 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(); } } } threadLocal 独立变量

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

package threand_dame1; /** * ThreadLocal 让每个线程都有自己内存来处理threadLocal * @author wangsong * @date: 2019年4月20日 下午6:12:19 */ class Res { // 生成序列号共享变量 public static ThreadLocal threadLocal = new ThreadLocal() { 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(); } } 线程池(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 // } // } // }); // } } }


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有