zoukankan      html  css  js  c++  java
  • JAVA多线程学习2--线程同步

    一、线程同步介绍

      同步:就是协同步调,按照预定的先后顺序执行。比如:你说完我再说。

      线程同步:访问同一个共享资源的时候多个线程能够保证数据的安全性、一致性。

    二、JAVA中实现线程同步的方法

      实现进程同步的方法是在共享竞争的资源上加锁,保证对资源的独占性。JAVA中通过关键字synchronized实现同步。看下面的例子

    package cn.edu.sdust.AsyTest;
    
    public class TestAsyn implements Runnable {
        
        Timer timer = new Timer();
    
        /**
         * @param args
         */
        
        public void run(){
            timer.add(Thread.currentThread().getName());
        }
        
        public static void main(String[] args) {
            // TODO Auto-generated method stub
    
            TestAsyn test1 = new TestAsyn();
            
            Thread t1 = new Thread(test1);
            Thread t2 = new Thread(test1);
            t1.setName("t1");
            t2.setName("t2");
            t1.start();
            t2.start();
        }
        
    }
    
    
    class Timer{
        
        int num=0;
        public  void add(String name){
            num++;
            try {
                Thread.sleep(1); //使当前线程睡眠,切换线程
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(name+"这是线程"+num);
            
        }
    }

    运行结果:

    t1这是线程2
    t2这是线程2

    分析:这是因为当线程t1执行后修改了num=1后睡眠,线程t2执行,修改num=2后睡眠,切换到线程t1执行,此时num已经为2,因此打印t1为第二个线程。显然这种结果不是我们想要的。

    如何保证数据的安全性,这里需要对共享资源加锁,实现线程同步。将共享资源add()方法加上关键字synchronized,保证资源的独占性。如下

    class Timer{
        
        int num=0;
        public synchronized void add(String name){
            num++;
            try {
                Thread.sleep(1); //使当前线程睡眠,切换线程
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(name+"这是线程"+num);
            
        }
    }

    修改后运行结果:

    t1这是线程1
    t2这是线程2

    解释:通过synchronized对add方法进行加锁,即使通过sleep使线程t1睡眠,线程t1仍然握有该资源的锁,因此t2不能执行,必须等t1执行完释放对资源的锁t2才能执行。(注:sleep与wait区别之一就是sleep后线程仍然握有资源的锁,而wait后线程将会放弃资源的锁,直到被唤醒后重新争夺资源的锁)

    三、synchronized的一些特点

      当线程握有synchronized加锁的资源的锁时,其他访问非加锁资源的线程能够执行。如下例子:

    public class ThreadAsynchronmous  implements Runnable{
    
        int n=100;
        
        public synchronized void   m1(){
            System.out.println("m1");
            n=1000;
            try{
            Thread.sleep(5000);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println("n="+n);
        }
        public  void m2() throws Exception{
            
            System.out.println("------"+n);
        }
        
        public void run(){
            m1();
        }
        /**
         * @param args
         * @throws Exception 
         */
        public static void main(String[] args) throws Exception {
            // TODO Auto-generated method stub
            ThreadAsynchronmous t=new ThreadAsynchronmous();
            
            new Thread(t).start();
            Thread.sleep(1000);        //让线程非主线程先执行,执行m1
            
        /*    for(int i=0; i<100; i++){
                System.out.println(i);
            }*/    
            t.m2();
        //System.out.println(n); } }

    运行结果:

    m1
    ------1000
    n=1000

    可以看到在加锁线程执行的同时,主线程仍然可以继续执行,非加锁资源仍然可以被执行。因此,不要在非加锁区对共享变量做修改。以防止数据的不安全、不一致。

  • 相关阅读:
    auto-sklearn案例解析二
    auto-sklearn案例解析二
    auto-sklearn案例解析一
    auto-sklearn案例解析一
    auto-sklearn简介
    auto-sklearn简介
    auto-sklearn手册
    auto-sklearn手册
    观念
    JDBC总结
  • 原文地址:https://www.cnblogs.com/vettel/p/3436348.html
Copyright © 2011-2022 走看看