目录
synchronized基础概念
- 多个线程步调一致的执行;
- 让多个线程争夺同一个对象的同步锁,任何对象都有唯一同步锁,只要创建一个对象,就会自有一个同步锁,注意,这里说的是对象,定义出来的基本类型是没有线程锁的;
- 多个线程对同一个对象操作,谁抢到锁谁执行,抢不到就等待,这样就保证没有脏数据;
为什么要用到线程同步
先来看一个例子:比如我现在有一个1-10个数字的整型集合,我现在对集合中2对倍数的数字进行加2,反复操作1000次,在另一个集合中进行数组打印:
public class SynchornizedTest { static int nums=1000; static int nums2=10; static ArrayList<Integer>list=new ArrayList<>(); public static void main(String[] args) { //集合中有1到10个数字 for(int i=1;i<=10;i++) { list.add(i); } SynchornizedThread t1=new SynchornizedThread(); t1.start(); SynchornizedThread2 t2=new SynchornizedThread2(); t2.start(); } //该线程对是2的倍数的数据进行反复的加2,操作1000次 static class SynchornizedThread extends Thread{ @Override public void run() { while(nums>0) { for(int i=0;i<list.size();i++) { if(list.get(i)%2==0) { list.set(i, list.get(i)+2); } } nums--; } } } //该线程取数据,打印10次 static class SynchornizedThread2 extends Thread{ @Override public void run() { while(nums2>0) { System.out.println(list); nums2--; } } } }
打印结果:很明显每次打印结果都是不同的,这里面就有脏数据,因为输出的话,应该输出线程1中操作完成的数据
synchronized的3中实现方式:
synchronized代码块 | 方式2 synchronized修饰方法 | 方式3 synchronized静态方法--属于类 |
synchronized(要操作的对象){
}
| synchronized void f(){
}
| static synchronized void f(){
}
|
方式1:synchronized代码块
测试:争夺指定对象list集合的锁
举例:对上面对例子进行线程同步
public class SynchornizedTest {
static int nums=1000;
static int nums2=10;
static ArrayList<Integer>list=new ArrayList<>();
public static void main(String[] args) {
//集合中有1到10个数字
for(int i=1;i<=10;i++) {
list.add(i);
}
SynchornizedThread t1=new SynchornizedThread();
t1.start();
SynchornizedThread2 t2=new SynchornizedThread2();
t2.start();
}
//该线程对是2的倍数的数据进行反复的加2,操作1000次
static class SynchornizedThread extends Thread{
@Override
public void run() {
synchronized (list) {
while(nums>0) {
for(int i=0;i<list.size();i++) {
if(list.get(i)%2==0) {
list.set(i, list.get(i)+2);
}
}
nums--;
}
}
}
}
//该线程取数据,打印10次
static class SynchornizedThread2 extends Thread{
@Override
public void run() {
synchronized (list) {
while(nums2>0) {
System.out.println(list);
nums2--;
}
}
}
}
}
打印结果:
方式2:synchronized修饰方法
测试:争夺当前对象(this)的锁,由对象r1调用add和get两个方法,按序执行
举例:比如我现在循环中打印一个数字是否是奇数:
public class SynchornizedTest2 {
public static void main(String[] args) {
R1 r1=new R1();
Thread t1=new Thread(r1);
t1.start();
//死循环检查是不是奇数
while(true) {
int i=r1.get();
if(i%2==1) {
System.out.println("出现奇数:"+i);
System.exit(0);//关闭虚拟机
}
}
}
static class R1 implements Runnable{
static int i;
public void add() {
i++;
i++;
}
public int get() {
return i;
}
@Override
public void run() {
while(true) {
add();
}
}
}
}
打印结果:按照上面的逻辑,其实不应该打印出奇数,因为i++应该执行两次,现在打印出来了,说明i++执行了一次就执行get方法了,这是main线程和t1线程冲突导致
通过给方法加锁,让t1线程拿到对应方法的锁执行完之后,再拿到另一个方法的锁,再继续执行
public class SynchornizedTest2 {
public static void main(String[] args) {
R1 r1=new R1();
Thread t1=new Thread(r1);
t1.start();
//死循环检查是不是奇数
while(true) {
int i=r1.get();
if(i%2==0) {
System.out.println("出现偶数:"+i);
System.exit(0);//关闭虚拟机
}
}
}
static class R1 implements Runnable{
static int i;
public synchronized void add() {
i++;
i++;
}
public synchronized int get() {
return i;
}
@Override
public void run() {
while(true) {
add();
}
}
}
}
这时再打印数据:
方式3:synchronized修饰类
测试:相当于抢当前类的锁
举例:对上面对例子进行思考,上面是只要一个线程t1,假如我现在有2个线程,在main线程里用r2进行get,看下执行结果:
public class SynchornizedTest2 {
public static void main(String[] args) {
R r1=new R();
Thread t1=new Thread(r1);
t1.start();
R r2=new R();
//死循环检查是不是奇数
while(true) {
int i=r2.get();
if(i%2==1) {
System.out.println("出现奇数:"+i);
System.exit(0);//关闭虚拟机
}
}
}
static class R implements Runnable{
static int i;
public synchronized void add() {
i++;
i++;
}
public synchronized int get() {
return i;
}
@Override
public void run() {
while(true) {
add();
}
}
}
}
输出:依然出现里奇数,为什么呢?因为此时虽然有synchronized来对方法进行修饰,但是此时已经不是一个对象进行操作,方法2只是争夺当前对象(this)的锁;
此时对代码进行处理:
public class SynchornizedTest2 {
public static void main(String[] args) {
R r1=new R();
Thread t1=new Thread(r1);
t1.start();
R r2=new R();
//死循环检查是不是奇数
while(true) {
int i=r2.get();
if(i%2==0) {
System.out.println("出现偶数:"+i);
System.exit(0);//关闭虚拟机
}
}
}
static class R implements Runnable{
static int i;
public static synchronized void add() {
i++;
i++;
}
public static synchronized int get() {
return i;
}
@Override
public void run() {
while(true) {
add();
}
}
}
}
输出: