一:线程安全
当多个线程同时访问一个实例(对象或者方法)时,输入的行为是正确的,那么可以认为这个程序是线程安全的。
看下面这段代码,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种方式都可以实现类锁!
原文:http://www.cnblogs.com/warrior4236/p/7163617.html