1.sychronized同步方法
1.1方法内的变量为线程安全
“非线程安全”问题存在于"实例变量"中,如果方法内部的私有变量,则不存在"非线程安全"问题,所得结果也就是"线程安全"的了。
package sychronizedDemo;
public class HasSelfPrivateNum2 {
public void addI(String username){
int num=0;
if(username.equals("a")) {
num = 100;
System.out.println("a set over!");
}else{
num=200;
System.out.println("b set over");
}
System.out.println(username+"num="+num);
}
}
class ThreadA1 extends Thread{
private HasSelfPrivateNum2 numRef;
public ThreadA1(HasSelfPrivateNum numRef){
this.numRef=numRef;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
class ThreadB1 extends Thread{
private HasSelfPrivateNum2 numRef;
public ThreadB1(HasSelfPrivateNum2 numRef){
this.numRef=numRef;
}
@Override
public void run() {
super.run();
numRef.addI("b");
}
}
class Run1{
public static void main(String[] args) {
HasSelfPrivateNum2 numRef=new HasSelfPrivateNum2();
ThreadA1 athread=new ThreadA1(numRef);
athread.start();
ThreadB1 bthread=new ThreadB1(numRef);
bthread.start();
}
}
结果如下:
可见,方法中的变量不存在线程安全问题,永远都是线程安全的。这是方法内部的变量是私有的特性造成的。
2.2实例变量非线程安全
如果多个线程共同访问1个对象中的实例变量,则有可能出现"非线程安全"问题。
package sychronizedDemo;
public class HasSelfPrivateNum {
private int num=0;
public void addI(String username){
if(username.equals("a")) {
num = 100;
System.out.println("a set over!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
num=200;
System.out.println("b set over");
}
System.out.println(username+"num="+num);
}
}
class ThreadA extends Thread{
private HasSelfPrivateNum numRef;
public ThreadA(HasSelfPrivateNum numRef){
this.numRef=numRef;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
class ThreadB extends Thread{
private HasSelfPrivateNum numRef;
public ThreadB(HasSelfPrivateNum numRef){
this.numRef=numRef;
}
@Override
public void run() {
super.run();
numRef.addI("b");
}
}
class Run{
public static void main(String[] args) {
HasSelfPrivateNum numRef=new HasSelfPrivateNum();
ThreadA athread=new ThreadA(numRef);
athread.start();
ThreadB bthread=new ThreadB(numRef);
bthread.start();
}
}
结果为:
本实验是两个线程同时访问一个没有同步的方法,如果两个线程同时访问对象中的实例变量,则有可能出现"非线程安全"问题。解决方法:只需要在public void addI(String username)方法前加入关键字sychronized即可。
2.3多个对象多个锁
例2.1、2.2介绍的都是多个对象一个锁的情况,那么多个对象多个锁会出现什么情况呢?
将上例中创建两个HasSelfPrivateNum()对象,代码如下:
public class HasSelfPrivateNum {
private int num=0;
synchronized public void addI(String username){
if(username.equals("a")) {
num = 100;
System.out.println("a set over!");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
num=200;
System.out.println("b set over");
}
System.out.println(username+" num="+num);
}
}
class ThreadA extends Thread{
private HasSelfPrivateNum numRef;
public ThreadA(HasSelfPrivateNum numRef){
this.numRef=numRef;
}
@Override
public void run() {
super.run();
numRef.addI("a");
}
}
class ThreadB extends Thread{
private HasSelfPrivateNum numRef;
public ThreadB(HasSelfPrivateNum numRef){
this.numRef=numRef;
}
@Override
public void run() {
super.run();
numRef.addI("b");
}
}
class Run{
public static void main(String[] args) {
HasSelfPrivateNum numRef1=new HasSelfPrivateNum();
HasSelfPrivateNum numRef2=new HasSelfPrivateNum();
ThreadA athread=new ThreadA(numRef1);
athread.start();
ThreadB bthread=new ThreadB(numRef2);
bthread.start();
}
}
结果为:
发现运行的结果是异步的交叉结果,明明使用了synchronized关键字为什么会这样呢?
关键字synchronized取得的锁都是对象锁,而不是把一段代码或者方法当成锁;
所以在实例中,因为是两个对象,所以调用这两个对象创建了相对于持有该方法的对象创建了锁LOCK,所以会产生两个锁,因此没有产生同步的效果。
2.4synchronized方法和锁对象
2.4.1 排队执行
public class MyObject{
public void methodA(){
try {
System.out.println("begin methodA threadName="+Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadA extends Thread{
private MyObject object;
public ThreadA(MyObject object){
super();
this.object=object;
}
@Override
public void run() {
super.run();
object.methodA();
}
}
class ThreadB extends Thread{
private MyObject object;
public ThreadB(MyObject object){
super();
this.object=object;
}
@Override
public void run() {
super.run();
object.methodA();
}
}
class Run{
public static void main(String[] args) {
MyObject object=new MyObject();
ThreadA threadA=new ThreadA(object);
ThreadB threadB=new ThreadB(object);
threadA.setName("A");
threadB.setName("B");
threadA.start();
threadB.start();
}
}
结果是:
修改MyObject代码为:
public class MyObject{
synchronized public void methodA(){
try {
System.out.println("begin methodA threadName="+Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
得到结果:
通过对比,通过调用关键字synchronized声明的方法一定是排队运行的。
但是只有共享资源的读写需要进行同步化,如果不是共享资源,那么根本没有同步的必要。
2.4.2相同对象不同方法的Lock
(1)如果我们一个对象中有两个方法,其中一个对象上锁,另一个对象不上锁。那么同时访问这两个对象,会出现同步效果吗?实验:
public class MyObject{
synchronized public void methodA() {
try {
System.out.println("begin methodA threadName=" + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end " + Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void methodB(){
try {
System.out.println("begin method threadName="+Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ThreadA extends Thread{
private MyObject object;
public ThreadA(MyObject object){
super();
this.object=object;
}
@Override
public void run() {
super.run();
object.methodA();
}
}
class ThreadB extends Thread{
private MyObject object;
public ThreadB(MyObject object){
super();
this.object=object;
}
@Override
public void run() {
super.run();
object.methodB();
}
}
class Run{
public static void main(String[] args) {
MyObject object=new MyObject();
ThreadA threadA=new ThreadA(object);
ThreadB threadB=new ThreadB(object);
threadA.setName("A");
threadB.setName("B");
threadA.start();
threadB.start();
}
}
结果:
我们发现并没有产生同步的效果。 虽然线程A先持有Object对象的锁。但是线程B完全可以通过异步调用synchronized类型的方法。
修改methodB()方法前加上synchronized关键字:
synchronized public void methodB(){
try {
System.out.println("begin method threadName="+Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("end "+Thread.currentThread().getName());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
结果是:
结论:1)A线程先持有Object对象的Object锁,B线程可以以异步的方式调用Object对象中的非synchronized类型的方法。
2) A线程现持有Object对象的锁,B线程同时调用Object对象中的同步锁类型的方法则需要等待。
2.5 脏读
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(1000);
this.password=password;
System.out.println("setValue method thread name="+Thread.currentThread().getName()+" username="+username+"password="+password);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void getValue(){
System.out.println("getValue method thread name="+Thread.currentThread().getName()+" username="+username+" passeord="+password);
}
}
class ThreadA1 extends Thread{
private PublicVar publicVar;
public ThreadA1(PublicVar publicVar){
this.publicVar=publicVar;
}
@Override
public void run() {
publicVar.setValue("B","BB");
}
}
class Test{
public static void main(String[] args) throws InterruptedException {
PublicVar publicVar=new PublicVar();
ThreadA1 threadA1=new ThreadA1(publicVar);
threadA1.start();
Thread.sleep(200);
publicVar.getValue();
}
}
结果:
方法setValue()和getValue()依次执行,但是却发生了脏读现象。只要将getValue()方法上加入同步锁就可以了。
因为setValue()拿到了对象锁,getValue()在运行时要等待setValue()释放对象锁才能执行。