1、多线程编程方式
- 创建资源类
- 创建资源类
- 资源类里创建同步方法、同步代码块
- 高内聚低耦合
2、为什么需要Synchronized?
编写一个基本的多线程操作例子
/**
* 给成员变量赋值操作
* 资源类
*/
public class Task {
//成员变量存储在堆内存里面,多个线程访问同一个堆内存,
//即多个线程可以同时修改num的值,这样会导致线程安全问题
private int num=0;
public void changeNum(boolean flag){
if(flag){
num = 88;
System.out.println(Thread.currentThread().getName() + "=====" + "begin");
System.out.println(Thread.currentThread().getName() + "=====" + num);
System.out.println(Thread.currentThread().getName() + "=====" + "over");
}else{
num = 66;
System.out.println(Thread.currentThread().getName() + "=====" + "begin");
System.out.println(Thread.currentThread().getName() + "=====" + num);
System.out.println(Thread.currentThread().getName() + "=====" + "over");
}
}
}
public class SynchronizedTest01 {
public static void main(String[] args) {
//多个线程控制一个对象(一个资源类)
Task task = new Task();
Thread t1 = new Thread(){
public void run(){
task.changeNum(true);
}
};
Thread t2 = new Thread(){
public void run(){
task.changeNum(false);
}
};
t1.start();
t2.start();
}
}
结果:
Thread-1=====begin
Thread-0=====begin
Thread-1=====66
Thread-0=====66
Thread-1=====over
Thread-0=====over
分析:
可以发现数据跟我们所想的并不一致,两个线程都打印出num等于66,这就出现了线程安全的问题,出现这个问题的原因是成员变量存储在堆内存中,两个线程共享堆内存,即两个线程可以对同一个num进行修改,如果num定义在方法中,那么就存在栈中,线程各自就会在栈中对其修改,则不会出现线程不安全的问题。
程序执行分析:
cpu执行t1线程,将num修改为88,之后cpu开始执行t2线程,将num修改为66,打印出66,cpu开始执行t1线程,打印num的值,此时num的值是66。
内存图解:
3、同步和异步
比如你要给A,B,C三人发消息
同步:先给A发,等A回复后,再给B发,等B回复后,再给C发,排队等待
异步:直接给A,B,C发消息,中间不需要等某人回复之后再给其他人发消息,不用排队等待
要想解决上述线程不安全的问题,可以将方法定义为同步方法
public synchronized void changeNum(boolean flag)
在方法上加入synchronized关键字,这样在执行多个线程时看哪个线程先执行这个方法,假设有t1,t2,t3三个线程中都调用了changeNum方法,t1线程先执行了这个方法,那么t1会先在Task对象上面加锁,加锁后,别的线程就无法执行当前Task对象上的changeNum方法,直到t1执行结束changeNum方法之后,t2,t3中的一个线程才可以执行这个方法,这就保证了在某个时间段内只有一个线程执行changeNum方法,解决了线程安全问题。
注意:synchronized锁住的是当前对象,如果t1线程和t2线程里面是不同的对象,则不需要同步,因为不会发生线程安全问题。如下代码:
public class SynchronizedTest01 {
public static void main(String[] args) {
//创建两个Task对象
Task task1 = new Task();
Task task2 = new Task();
//两个线程t1和t2使用的是不同的Task对象,不会发生线程安全问题
Thread t1 = new Thread(){
public void run(){
task1.changeNum(true);
}
};
Thread t2 = new Thread(){
public void run(){
task2.changeNum(false);
}
};
t1.start();
t2.start();
}
}
//此时并不会出现线程不安全的问题,每个线程执行各自的资源类,并不会出现线程不安全的问题