synchronized同步方法
脏读
在多个线程对同一个对象中的实例变量进行并发访问的时候,取到的数据可能是被更改过的,称之为“脏读”,这就是非线程安全的。解决的方法为synchronized关键字进行同步,使之操作变成同步而非异步。
public class PublicVar {
public String username = "A";
public String password = "AA";
synchronized public void setValue(String username, String password) {
try {
this.username = username;
Thread.sleep(5000);
this.password = password;
System.out.println("setValue method thread name="
+ Thread.currentThread().getName() + " username="
+ username + " password=" + password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public void getValue() {//不加同步将会造成脏读
System.out.println("getValue method thread name="
+ Thread.currentThread().getName() + " username=" + username
+ " password=" + password);
}
}
public class ThreadA extends Thread {
private PublicVar publicVar;
public ThreadA(PublicVar publicVar) {
super();
this.publicVar = publicVar;
}
@Override
public void run() {
super.run();
publicVar.setValue("B", "BB");
}
}
public class Test {
public static void main(String[] args) {
try {
PublicVar publicVarRef = new PublicVar();
ThreadA thread = new ThreadA(publicVarRef);
thread.start();
Thread.sleep(200);// 打印结果受此值大小影响
publicVarRef.getValue();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
未加同步锁
加了同步锁
多个对象监视器多个锁
多个线程访问多个对象,JVM会创建多个锁
public class Test {
public static void main(String[] args) {
TestRunable public1 = new TestRunable();
TestRunable public2 = new TestRunable();
ThreadA athread = new ThreadA(public1);
athread.start();
ThreadB bthread = new ThreadA(public2);
bthread.start();
}
}
上面示例是两个线程访问同一个类的两个不同实例对象,效果是异步的方式运行。即时加了同步锁也是异步执行,因为创建了两个对象,将会产生两把锁。
锁重入
当一个线程得到一个对象锁后,再次请求此对象锁是可以再次得到该对象锁的。就是在自己已经获得该对象锁的前提下,还可以再次获取自己的锁。可重入锁也支持在父子类继承的环境中。
public class MyThread extends Thread {
@Override
public void run() {
Service service = new Service();
service.service1();
}
}
public class Service {
synchronized public void service1() {
System.out.println("service1");
service2();
}
synchronized public void service2() {
System.out.println("service2");
service3();
}
synchronized public void service3() {
System.out.println("service3");
}
}
public class Run {
public static void main(String[] args) {
MyThread t = new MyThread();
t.start();
}
}
synchronized同步语句块
同步语句块的好处
public class Task {
private String getData1;
/**
* synchronized public void doLongTimeTask(){}
* 如果同步锁在方法上
* A线程执行的时候会锁住3秒钟,然B线程再执行,效率太低
* 所以同步代码块是效率最高的方法
*/
public void doLongTimeTask() {
try {
System.out.println("begin task");
Thread.sleep(3000);
synchronized (this) {
getData1 = Thread.currentThread().getName();
}
System.out.println(getData1);
System.out.println("end task");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class MyThread1 extends Thread {
private Task task;
public MyThread1(Task task) {
super();
this.task = task;
}
@Override
public void run() {
super.run();
task.doLongTimeTask();
}
}
public class MyThread2 extends Thread {
private Task task;
public MyThread2(Task task) {
super();
this.task = task;
}
@Override
public void run() {
super.run();
task.doLongTimeTask();
}
}
public class Run {
public static void main(String[] args) {
Task task = new Task();
MyThread1 thread1 = new MyThread1(task);
thread1.start();
MyThread2 thread2 = new MyThread2(task);
thread2.start();
}
}
如果同步方法的话,程序跑完大概在6秒钟左右,A线程B线程各用时3秒钟
如果同步语句块的话,让耗时的操作异步执行,那么程序跑完大概也就3秒钟,效率提升很高。
一半同步一半异步
把上面的Task类修改如
在synchronized 中就是同步,不在synchronized 中就是异步,可以运行看下结果
public class Task {
public void doLongTimeTask() {
for (int i = 0; i < 100; i++) {
System.out.println("nosynchronized threadName="
+ Thread.currentThread().getName() + " i=" + (i + 1));
}
System.out.println("");
synchronized (this) {
for (int i = 0; i < 100; i++) {
System.out.println("synchronized threadName="
+ Thread.currentThread().getName() + " i=" + (i + 1));
}
}
}
}
死锁
不同的线程都在等待不可能释放的锁,从而导致所有任务都无法完成,造成线程的假死。
public class DealThread implements Runnable {
public String username;
public Object lock1 = new Object();
public Object lock2 = new Object();
public void setFlag(String username) {
this.username = username;
}
@Override
public void run() {
if (username.equals("a")) {
synchronized (lock1) {
try {
System.out.println("username = " + username);
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("按lock1->lock2代码顺序执行了");
}
}
}
if (username.equals("b")) {
synchronized (lock2) {
try {
System.out.println("username = " + username);
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("按lock2->lock1代码顺序执行了");
}
}
}
}
}
public class Run {
public static void main(String[] args) {
try {
DealThread t1 = new DealThread();
t1.setFlag("a");
Thread thread1 = new Thread(t1);
thread1.start();
Thread.sleep(100);
t1.setFlag("b");
Thread thread2 = new Thread(t1);
thread2.start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
互相等待,导致线程假死
volatile关键字
volatile关键字提示线程每次从共享内存中读取变量,而不是私有内存。
适用场合是在多个线程中可以感知实例变量被更改了。
在JVM被设置成-server服务器模式运行时,为了线程运行的效率,线程一直在私有堆栈中。其他线程更改的实例变量值却会更新在公共堆栈中。
解决异步死循环
public class RunThread extends Thread {
//volatile 关键字 isRunning变量将从公共堆栈中取值
volatile private boolean isRunning = true;
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
@Override
public void run() {
System.out.println("进入run了");
while (isRunning == true) {
}
System.out.println("线程被停止了!");
}
}
public class Run {
public static void main(String[] args) {
try {
RunThread thread = new RunThread();
thread.start();
Thread.sleep(1000);
thread.setRunning(false);
System.out.println("已经赋值为false");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
在-server服务器模式中运行
volatile的非原子性(synchronized的代码块有volatile同步的功能)
volatile关键字增加了多线程之间实例变量的可见性,但是不能保证同步性
public class Service {
private boolean isContinueRun = true;
public void runMethod() {
String anyString = new String();
while (isContinueRun == true) {
//synchronized 可以使多个线程访问统一资源具有同步性
//可以同步 工作内存中的私有变量和公共内存中的变量
synchronized (anyString) {
}
}
System.out.println("停下来了!");
}
public void stopMethod() {
isContinueRun = false;
}
}
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.runMethod();
}
}
public class ThreadB extends Thread {
private Service service;
public ThreadB(Service service) {
super();
this.service = service;
}
@Override
public void run() {
service.stopMethod();
}
}
public class Run {
public static void main(String[] args) {
try {
Service service = new Service();
ThreadA a = new ThreadA(service);
a.start();
Thread.sleep(1000);
ThreadB b = new ThreadB(service);
b.start();
System.out.println("已经发起停止的命令了!");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
在-server服务器模式中运行
注意事项
**synchronized关键字锁住变量的时候最好不要用String类型的,要考虑字符串常量池的问题
例如:
String str="aaa";
String str1="aaa";
//java中的字符串常量池会导致同步失效
//System.out.print(str==str1);//true
//所以最好用 Object o=new Object();
synchronized(str){
//TODO
}
synchronized关键字加到static静态方法上是给Class类加锁
Class锁可以对类的所有对象实例起作用。