首页 > 代码库 > 并发编程基础2

并发编程基础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种方式都可以实现类锁!

并发编程基础2