zoukankan      html  css  js  c++  java
  • 并发编程基础2

    一:线程安全

    当多个线程同时访问一个实例(对象或者方法)时,输入的行为是正确的,那么可以认为这个程序是线程安全的。

    看下面这段代码,10个线程同时访问1个实例,那么运行结果会怎样呢?

    /**
     * 
     */
    package com.day1;
    
    import java.io.UnsupportedEncodingException;
    
    /**
     * @author Administrator 线程安全:多个线程访问同一个实例时,如果输入的行为正确,那么我们可以称作线程安全,
     *         下面的实例,10个线程访问同一个对象,存在线程安全问题,但是如果我们在可能存在安全问题
     *         的方法或者代码片段上加上锁synchronized,就可以避免线程安全问题
     *         a:线程获取cpu的处理权,是随机的,可以配置优先级,而不是按照程序的书写顺序
     *         b:在使用synchronized关键字时,应该尽量缩小使用范围,这样可以提高系统处理效率(前提是避免线程安全问题)
     */
    public class MultiThread2 extends Thread {
    
    	private int num = 10;
    
    	public void run() {
    		num--;
    		String threadName = Thread.currentThread().getName();
    		System.out.println(threadName + ":::" + num);
    	}
    
    	public static void main(String[] args) throws UnsupportedEncodingException {
    		MultiThread2 task = new MultiThread2();
    		Thread thread1 = new Thread(task, "线程1");
    		Thread thread2 = new Thread(task, "线程2");
    		Thread thread3 = new Thread(task, "线程3");
    		Thread thread4 = new Thread(task, "线程4");
    		Thread thread5 = new Thread(task, "线程5");
    		Thread thread6 = new Thread(task, "线程6");
    		Thread thread7 = new Thread(task, "线程7");
    		Thread thread8 = new Thread(task, "线程8");
    		Thread thread9 = new Thread(task, "线程9");
    		Thread thread10 = new Thread(task, "线程10");
    		thread1.start();
    		thread2.start();
    		thread3.start();
    		thread4.start();
    		thread5.start();
    		thread6.start();
    		thread7.start();
    		thread8.start();
    		thread9.start();
    		thread10.start();
    	}
    }
    

     运行结果:

    线程2:::8
    线程5:::6
    线程1:::7
    线程3:::7
    线程7:::5
    线程4:::4
    线程9:::3
    线程6:::2
    线程8:::1
    线程10:::0
    

     通过运行结果,可以看出存在线程安全问题 ,因为10个线程共享一个成员变量num的值,都去操作它

    如果想是运行行为正确,可以在方法上面加个同步synchronized关键字:

    public synchronized void run() {
    		num--;
    		String threadName = Thread.currentThread().getName();
    		System.out.println(threadName + ":::" + num);
    	}
    
    线程1:::9
    线程3:::8
    线程4:::7
    线程7:::6
    线程2:::5
    线程5:::4
    线程6:::3
    线程8:::2
    线程9:::1
    线程10:::0
    

     这时运行结果就是正确的了,顺序num的值是对的,有人会说线程输出顺序不对,这是因为线程

    的执行要看cpu的分配,是随机的,不是按照程序书写顺序。

    一般情况下,在保证同步安全的前提下,要尽可能的缩小锁定的范围,这样可以提高系统的处理效率,如下:

    public  void run() {
    		synchronized (this) {
    			System.out.println(Thread.currentThread().getName() + ":::" + --num);
    		}
    	}
    
    线程1:::9
    线程2:::8
    线程4:::7
    线程6:::6
    线程8:::5
    线程3:::4
    线程10:::3
    线程5:::2
    线程7:::1
    线程9:::0
    

    运行结果依然是正确的。

    二:同步与异步

    只有多个线程存在同享时,才需要在同步锁synchronized,不共享的情况下不需要加,一个类中如果有的方法是同步的

    ,有的方法是异步的,那么当多个线程访问该实例时(一个实例),会发生什么情况呢?

    /**
     * 
     */
    package com.day1;
    
    /**
     * @author Administrator
     * 两个线程访问同一个实例,如果访问的都是同步方法(加synchronized关键字,可以是同一个方法,也可能是
     * 两个方法),那么一个线程正在执行程序时,另一个线程处于阻塞状态。
     * 如果一个线程访问同步方法,另一个线程访问异步方法,那么不存在阻塞问题。
     */
    public class MultiThread3 {
    
    	private synchronized void method1() {
    		System.out.println("running method1...");
    		try {
    			Thread.sleep(1000L);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    	private  void method2() {
    		System.out.println("running method2...");
    	}
    
    	public static void main(String[] args) {
    		MultiThread3 task = new MultiThread3();
    		Thread t1 = new Thread(new Runnable(){
    			@Override
    			public void run() {
    				task.method1();
    			}
    		});
    		Thread t2 = new Thread(new Runnable(){
    			@Override
    			public void run() {
    				task.method2();
    			}
    		});
    		t1.start();
    		t2.start();
    	}
    
    }
    

     两个线程访问同一个类时,如果一个线程访问同步方法,另一个线程访问异步方法时,那么不存在线程阻塞。

    running method1...
    running method2...
    

     运行结果:

    两行结果都打印完之后,然后jvm等待1s后停止,说明两个线程都是正常执行,没有存在阻塞问题。

    如果把第二个方法上面也加上synchronized关键字,

    private synchronized void method2() {
    		System.out.println("running method2...");
    	}
    

    在运行:

    running method1...
    running method2...
    

     那么运行结果是,先输出第一行,然后等待1s后又输出了第二行,说明第一个线程访问method1时,

    第二个线程没有拿到对象锁,处于线程等待状态,待一个线程执行完毕后,释放对象锁,然后第二个

    线程执行method2方法。

    三:多个线程多个实例控制并发

    如果有多个线程访问多个 实例,那么他们之间是没有任何关系的,不存在线程安全问题,但是如果

    也想使用锁控制它们,则需要类锁

    /**
     * 
     */
    package com.day1;
    
    /**
     * @author Administrator
     * 两个线程访问同一个实例,如果访问的都是同步方法(加synchronized关键字,可以是同一个方法,也可能是
     * 两个方法),那么一个线程正在执行程序时,另一个线程处于阻塞状态。
     * 如果一个线程访问同步方法,另一个线程访问异步方法,那么不存在阻塞问题。
     */
    public class MultiThread3 {
    
    	private synchronized void method1() {
    		System.out.println("running method1...");
    		try {
    			Thread.sleep(1000L);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    	private synchronized void method2() {
    		System.out.println("running method2...");
    	}
    
    	public static void main(String[] args) {
    		final MultiThread3 task1 = new MultiThread3();
    		final MultiThread3 task2 = new MultiThread3();
    		Thread t1 = new Thread(new Runnable(){
    			@Override
    			public void run() {
    				task1.method1();
    			}
    		});
    		Thread t2 = new Thread(new Runnable(){
    			@Override
    			public void run() {
    				task2.method2();
    			}
    		});
    		t1.start();
    		t2.start();
    	}
    
    }
    

     运行结果:

    running method1...
    running method2...
    

     两行数据同时输出,等待1s然后停止,说明不存在线程安全问题,因为这里是两个实例,不同的锁

    如果也想让它们串行执行,则需要使用类锁

    1:在两个方法上加上static,则两个同步方法的锁变成了类的字节码文件

    private synchronized static void method1() {
    		System.out.println("running method1...");
    		try {
    			Thread.sleep(1000L);
    		} catch (InterruptedException e) {
    			e.printStackTrace();
    		}
    	}
    
    	private synchronized static void method2() {
    		System.out.println("running method2...");
    	}
    

    2:使用同步代码块,将类的字节码文件作为锁

    private  void method1() {
    		synchronized (this.getClass()) {
    			System.out.println("running method1...");
    			try {
    				Thread.sleep(1000L);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    	private  void method2() {
    		synchronized (this.getClass()) {
    			System.out.println("running method2...");
    		}
    	}
    

     3:定义一个静态的对象,作为类锁

    因为静态的对象,所有的实例都是共享这个对象,所以在同一时刻只能有一个实例可以持有这个锁

    private static final Object lock = new Object();
    
    	private  void method1() {
    		synchronized (lock) {
    			System.out.println("running method1...");
    			try {
    				Thread.sleep(1000L);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    	}
    
    	private  void method2() {
    		synchronized (lock) {
    			System.out.println("running method2...");
    		}
    	}
    

     以上3种方式都可以实现类锁!

  • 相关阅读:
    C# 数据权限缓存
    .net core平台使用遇到的坑
    @RenderBody @RenderPage @RenderSection
    _ViewStart.cshtml介绍
    Git中的AutoCRLF与SafeCRLF换行符问题
    select fotr update
    索引的区分度
    索引最左匹配原则
    mysql索引相关知识
    锁-乐观锁和悲观锁
  • 原文地址:https://www.cnblogs.com/warrior4236/p/7163617.html
Copyright © 2011-2022 走看看