• java核心知识点学习----多线程并发之线程同步


    1.什么是线程同步?

     多线程编程是很有趣的事情,它很容易出现"错误情况",这种情况不是由编码造成的,它是由系统的线程调度造成的,当使用多个线程来访问同一个数据时,很容易出现"偶然情况",出现线程安全问题.

    线程安全问题最常见的就是银行取钱问题,铁路售票问题,必须保证甲方在操作数据时候,己方不会影响甲方.类似于公共厕所,一个人占一个坑.

    2.下面的例子,将说明为什么要保证线程安全?

    package com.amos.concurrent;
    /** 
    * @ClassName: ThreadSynchronizedTest 
    * @Description: 多线程并发之线程同步
    * @author: amosli
    * @email:hi_amos@outlook.com
    * @date Apr 20, 2014 2:44:29 PM  
    */
    public class ThreadSynchronizedTest {
        public static void main(String[] args) {
            new ThreadSynchronizedTest().init();
        }
        
        private void init() {
            final OutPuter outPuter = new OutPuter();
            //新建一个线程A
            new Thread(new Runnable() {
                public void run() {
                    while (true) {
                        //休息10ms
                        try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
                        outPuter.output("hi_amos");//输出
                    }
                }
            }).start();
            //线程B
            new Thread(new Runnable() {
                public void run() {
                    while (true) {
                    try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
                    outPuter.output("amosli");
                    }
                }
            }).start();
        }
    
        class OutPuter {
            //输出name,逐个字节读取,并输出
            public void output(String name) {
                int length = name.length();
                synchronized (name) {
                    for (int i = 0; i < length; i++) {
                        System.out.print(name.charAt(i));
                    }
                    System.out.println();
                }
            }
        }
    }

    输出结果如下图所示:

     

    在多次执行上面的代码后就会发现出现问题了,这是因为线程A和线程B在执行output方法时,系统调度出现了问题,导致了上面的问题,这种情况出现的概率相对较小,但这种小概率的事件也是要解决的.下面将介绍如何解决这种问题.

    3.解决方法1--同步代码块

    只需要将上面的方法加上synchronized关键字即可

    public void output2(String name) {
                    int length = name.length();
                    synchronized (this) {
                      for (int i = 0; i < length; i++) {
                            System.out.print(name.charAt(i));
                        }
                        System.out.println();    
                    }
            }

    在要多次访问的代码块前加上synchronized关键字,即表示加上排队系统,线程A只有等线程B执行完了才能访问同一个代码块.

    这里要注意this,this表示的是当前对象,这里this也可以用Outputer.class代替.

    4.解决方法2--同步方法

    public synchronized void output(String name) {
                int length = name.length();
                    for (int i = 0; i < length; i++) {
                        System.out.print(name.charAt(i));
                    }
                    System.out.println();
            }

    同步方法,即是在要多次访问的方法前面加上synchronized关键字.

    注意:

    1.synchronized关键字可以修饰方法,代码块,但不能修饰构造器,属性等;

    2.同时synchronized关键字最好一个方法中只用一次,否则可能造成死锁.

    3.任何时刻只能有一个线程可以获得对同步监视器的锁定,当同步代码块执行完成后,该线程会释放对该同步监视器的锁定.

     上面的代码可以改写如下:

    package com.amos.concurrent;
    
    /** 
    * @ClassName: ThreadSynchronizedTest 
    * @Description: 多线程并发之线程安全
    * @author: amosli
    * @email:hi_amos@outlook.com
    * @date Apr 20, 2014 2:44:29 PM  
    */
    public class ThreadSynchronizedTest {
        public static void main(String[] args) {
            new ThreadSynchronizedTest().init();
        }
        
        private void init() {
            final OutPuter outPuter = new OutPuter();
            //新建一个线程
            new Thread(new Runnable() {
                public void run() {
                    while (true) {
                        //休息10ms
                        try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
                        outPuter.output("hi_amos");//输出
                    }
                }
            }).start();
            
            new Thread(new Runnable() {
                public void run() {
                    while (true) {
                    try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
                    outPuter.output("amosli");
                    }
                }
            }).start();
        }
    
        static class OutPuter {
            //输出name,逐个字节读取,并输出
            public synchronized void output(String name) {
                int length = name.length();
                    for (int i = 0; i < length; i++) {
                        System.out.print(name.charAt(i));
                    }
                    System.out.println();
            }
            public void output2(String name) {
                    int length = name.length();
                    synchronized (OutPuter.class) {
                        for (int i = 0; i < length; i++) {
                            System.out.print(name.charAt(i));
                        }
                        System.out.println();    
                    }
            }
            public synchronized static void output3(String name) {
                int length = name.length();
                    for (int i = 0; i < length; i++) {
                        System.out.print(name.charAt(i));
                    }
                    System.out.println();    
            }
        }
    }
    View Code
  • 相关阅读:
    C++中重载、重定义、重写概念辨析
    虚函数,抽象函数
    取出一个int的每一位,用算法
    Typedef和define
    枚举
    基于 ThinkPHP 3.2.3 的页面静态化功能的实现
    骑行在华盛顿 针对320万次共享单车骑行数据的分析
    骑行在华盛顿 针对320万次共享单车骑行数据的分析
    分析了10个垂直行业后,告诉你大数据应用面临哪些挑战
    分析了10个垂直行业后,告诉你大数据应用面临哪些挑战
  • 原文地址:https://www.cnblogs.com/amosli/p/3676542.html
Copyright © 2020-2023  润新知