synchronized方法
常识
- 访问局部变量不会产生线程安全问题
- 访问实例变量会产生线程安全问题
- 多个对象多个锁
两个线程是异步执行的,因为在main中创建了两个对象,每个对象各自对应一个锁,即线程和锁对象是一对一的关系,每个线程执行自己所属业务对象的同步方法,不存在争抢关系。只有当线程和业务对象是多对一的关系时才会出现线程安全问题进而使用同步锁。
public class ThreadDemo01 {
public static void main(String[] args) {
pNum pnum1 = new pNum();
pNum pnum2 = new pNum();
Thread1A t1 = new Thread1A(pnum1);
t1.start();
Thread1B t2 = new Thread1B(pnum2);
t2.start();
}
}
class pNum {
private int num;
synchronized public void add(String name) {
try {
if (name.equals("a")) {
num = 100;
System.out.println("a set over");
} else {
num = 200;
System.out.println("b set over");
}
System.out.println(name + " num=" + num);
Thread.sleep(2000);
} catch (
InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Thread1A extends Thread {
private pNum pnum;
public Thread1A(pNum pnum) {
this.pnum = pnum;
}
public void run() {
pnum.add("a");
}
}
class Thread1B extends Thread {
private pNum pnum;
public Thread1B(pNum pnum) {
this.pnum = pnum;
}
public void run() {
pnum.add("b");
}
}
- 方法前加synchronized锁住的其实是对象而不是方法,即java中只有“将对象作为锁”而没有“将方法作为锁这种说法”。
- “锁”就是“对象”,“对象”可以映射成“锁”,哪个线程拿到这个锁,就可以执行同步方法。
- 将对象当做锁
package thread_part2;
public class ThreadDemo2 {
public static void main(String[] args) {
LockDemo ld = new LockDemo();
Thread2A t2a = new Thread2A(ld);
t2a.setName("A");
t2a.start();
Thread2B t2b = new Thread2B(ld);
t2b.setName("B");
t2b.start();
}
}
class LockDemo {
/*
* public void methodL() throws Exception {
* System.out.println("thread name is "+Thread.currentThread().getName());
* Thread.sleep(4000); }
*/
synchronized public void methodL() throws Exception {
System.out.println("thread name is "+Thread.currentThread().getName());
Thread.sleep(4000);
System.out.println("end");
}
}
class Thread2A extends Thread {
private LockDemo lc;
public Thread2A(LockDemo lc) {
// TODO Auto-generated constructor stub
this.lc = lc;
}
public void run() {
try {
lc.methodL();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
class Thread2B extends Thread {
private LockDemo lc;
public Thread2B(LockDemo lc) {
// TODO Auto-generated constructor stub
this.lc = lc;
}
public void run() {
try {
lc.methodL();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
不加锁
加锁
- 脏读 set有锁 get没锁
- 线程代码出现异常自动释放锁,sleep,suspend不释放锁。
- 继承父类后重写子类方法如果想要继续保持同步特性需要在重写的方法前加synchronized。
volatile 关键字–只能修饰变量
可见性
一个线程更改数据另一个线程是否能看见
package thread_part2;
public class ThreadDemo3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
SynPrint sp = new SynPrint();
//一直在进行while循环,无法进入到后续set方法。
sp.printMethod();
System.out.println("停止打印");
sp.setFlagPrint(false);
}
}
class SynPrint {
private boolean flagPrint = true;
public boolean flagPrint() {
return flagPrint;
}
public void setFlagPrint(boolean flagP) {
this.flagPrint = flagP;
}
public void printMethod() {
while (flagPrint == true) {
System.out.println("run thread name is " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
- 主线程设置false
package thread_part2;
public class ThreadDemo3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
SynPrint sp = new SynPrint();
Thread t1 = new Thread(sp);
t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("停止打印");
sp.setFlagPrint(false);
}
}
/*
* class SynPrint {
*
* private boolean flagPrint = true;
*
* public boolean flagPrint() { return flagPrint; }
*
* public void setFlagPrint(boolean flagP) { this.flagPrint = flagP; }
*
* public void printMethod() { while (flagPrint == true) {
* System.out.println("run thread name is " + Thread.currentThread().getName());
* try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO
* Auto-generated catch block e.printStackTrace(); } } }
*/
class SynPrint implements Runnable{
private boolean flagPrint = true;
public boolean flagPrint() {
return flagPrint;
}
public void setFlagPrint(boolean flagP) {
this.flagPrint = flagP;
System.out.println("current set thread name is "+Thread.currentThread().getName());
}
public void printMethod() {
while (flagPrint == true) {
System.out.println("run thread name is " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Override
public void run() {
// TODO Auto-generated method stub
printMethod();
}
}
- 使用volatile解决死循环
package thread_part2;
public class ThreadDemo4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
SynPrintNew sp = new SynPrintNew();
sp.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sp.setFlagPrint(false);
System.out.println("已经赋值为false");
}
}
/*
* class SynPrint {
*
* private boolean flagPrint = true;
*
* public boolean flagPrint() { return flagPrint; }
*
* public void setFlagPrint(boolean flagP) { this.flagPrint = flagP; }
*
* public void printMethod() { while (flagPrint == true) {
* System.out.println("run thread name is " + Thread.currentThread().getName());
* try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO
* Auto-generated catch block e.printStackTrace(); } } }
*/
class SynPrintNew extends Thread{
//启动以后这个true 存在于公共堆栈和线程的私有堆栈当中,线程运行时一直在私有堆栈中取得true,set方法虽在被执行但是只是改变了公有栈中的变量。线程获取的值仍然是私有栈的true,所以会出现死循环。
private boolean flagPrint = true;
public boolean flagPrint() {
return flagPrint;
}
public void setFlagPrint(boolean flagP) {
this.flagPrint = flagP;
System.out.println("current set thread name is "+Thread.currentThread().getName());
}
/*
* public void printMethod() { while (flagPrint == true) {
* System.out.println("run thread name is " + Thread.currentThread().getName());
* try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO
* Auto-generated catch block e.printStackTrace(); } } }
*/
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("进入run了");
while(flagPrint == true) {
}
System.out.println("线程被终止了");
}
}
加入volatile关键字,强制线程从公共堆栈中取值。
原子性
- volatile修饰的i,进行i++这种操作时没有原子性。 (内存中取出i的值→计算i进行加法后的值→将新i值放到内存中),当进行到第二步其他线程也修改i值会出现脏读情况。
- synchronized 可以实现原子性
禁止重排序
- 什么是重排序
发生在代码之间没有依赖关系时。
如下代码测试方式:每输出一组符合要求的数值就删除该 if 判断结构
package thread_part2;
public class ThreadDemo5 {
static int x = 0;
static int y = 0;
static int a = 0;
static int b = 0;
static int count = 0;
public static void main(String[] args) throws InterruptedException {
for ( ; ; ) {
x=0;
y=0;
a=0;
b=0;
count++;
Thread t1 = new Thread(new Runnable() {
public void run() {
a = 1;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
x = b;
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
b = 1;
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
y = a;
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count+"(" + x + "," + y + ")");
//符合就删除
if(x==1 && y==0) {
System.out.println(count+"(" + x + "," + y + ")");
break;
}
//符合就删除
if(x==0 && y==0) {
System.out.println(count+"(" + x + "," + y + ")");
break;
}
//符合就删除
if(x==0 && y==1) {
System.out.println(count+"(" + x + "," + y + ")");
break;
}
//符合就删除
if(x==1 && y==1) {
System.out.println(count+"(" + x + "," + y + ")");
break;
}
}
}
}
此为重排序后输出的结果
- 性质
volatile之前、之后可重排;之前不能到之后,之后不能到之前;相当于一个屏障。
https://blog.youkuaiyun.com/yjp198713/article/details/78839698#commentBox