多線程訪問會有什麼問題??
public class TestMyThread extends Thread{
//共享變量
private int a = 5;
//重寫run方法
@Override
public void run(){
a--;
System.out.println("a is :"+ a);
}
public static void main(String[] args) {
//實列需要多線程的對象
TestMyThread myThread = new TestMyThread();
//創建多個線程實列
Thread t1 = new Thread(myThread,"t1");
Thread t2 = new Thread(myThread,"t2");
Thread t3 = new Thread(myThread,"t3");
Thread t4 = new Thread(myThread,"t4");
Thread t5 = new Thread(myThread,"t5");
//運行
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
結果:
a is :3
a is :3
a is :2
a is :1
a is :0a is :4
a is :3
a is :2
a is :1
a is :0
等等,你會發現每次出來的結果都不一樣,這就是多線程訪問的問題
怎樣解決???
package test19218;
import test517designpattern.test;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月18日
*/public class TestMyThread extends Thread{
//共享變量
private int a = 5;
//重寫run方法
@Override
public synchronized void run(){
a--;
System.out.println("a is :"+ a);
}
public static void main(String[] args) {
//實列需要多線程的對象
TestMyThread myThread = new TestMyThread();
//創建多個線程實列
Thread t1 = new Thread(myThread,"t1");
Thread t2 = new Thread(myThread,"t2");
Thread t3 = new Thread(myThread,"t3");
Thread t4 = new Thread(myThread,"t4");
Thread t5 = new Thread(myThread,"t5");
//運行
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
我們加了synchronize關鍵字
a is :4
a is :3
a is :2
a is :1
a is :0
結果是我們想要的。
那麼synchronize是鎖的對象myThread ,還是那一段代碼呢?
package test19218;
import test517designpattern.test;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月18日
*/public class TestMyThread extends Thread{
//共享變量
private int a = 5;
//重寫run方法
@Override
public synchronized void run(){
a--;
System.out.println("a is :"+ a);
}
public static void main(String[] args) {
//實列需要多線程的對象
TestMyThread myThread = new TestMyThread();
TestMyThread myThread2 = new TestMyThread();
//創建多個線程實列
Thread t1 = new Thread(myThread,"t1");
Thread t2 = new Thread(myThread2,"t2");
//運行
t1.start();
t2.start();
}
}
結果
a is :4
a is :4
說明鎖住的是代碼。如果是鎖住的對象,那麼就應該減兩次纔對!!!
/**
* 關鍵字synchronized取得的鎖都是對象鎖,而不是把一段代碼(方法)當做鎖,
* 所以代碼中哪個線程先執行synchronized關鍵字的方法,哪個線程就持有該方法所屬對象的鎖(Lock),
*
* 在靜態方法上加synchronized關鍵字,表示鎖定.class類,類一級別的鎖(獨佔.class類)。
* @author alienware
*
*/
如果同一個對象,兩個方法,一個用synchronize修飾,另一個不用,我訪問非synchronize修飾的方法是否需要等待,其實就是是否會阻塞?
package test19218;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月18日
*/public class TestMyThread02 {
String tag;
public TestMyThread02(String tag) {
this.tag = tag;
}public TestMyThread02() {
}// 共享變量
private int a = 5;// 方法
public synchronized void deNum(String tag) throws InterruptedException {
a--;
Thread.sleep(1000);
System.out.println(tag + "---a is :" + a);
}// 方法
public void adNum(String tag) {
a++;
System.out.println(tag + "---a is :" + a);
}public static void main(String[] args) {
final String t1 = "t1";
final String t2 = "t2";// 實列需要多線程的對象
final TestMyThread02 myThread01 = new TestMyThread02();
// final TestMyThread02 myThread02 = new TestMyThread02();// 創建多個線程實列
Thread th = new Thread(new Runnable() {@Override
public void run() {try {
myThread01.deNum(t1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
});Thread th1 = new Thread(new Runnable() {
@Override
public void run() {myThread01.adNum(t2);
}
});// 運行
th.start();
th1.start();
}}
結果
t2---a is :5
t1---a is :5
t2先執行了,所以並不會阻塞。
如果兩個方法都加上synchronize呢?
package test19218;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月18日
*/public class TestMyThread02 {
String tag;
public TestMyThread02(String tag) {
this.tag = tag;
}public TestMyThread02() {
}// 共享變量
private int a = 5;// 方法
public synchronized void deNum(String tag) throws InterruptedException {
a--;
Thread.sleep(1000);
System.out.println(tag + "---a is :" + a);
}// 方法
public synchronized void adNum(String tag) {
a++;
System.out.println(tag + "---a is :" + a);
}public static void main(String[] args) {
final String t1 = "t1";
final String t2 = "t2";// 實列需要多線程的對象
final TestMyThread02 myThread01 = new TestMyThread02();
// final TestMyThread02 myThread02 = new TestMyThread02();// 創建多個線程實列
Thread th = new Thread(new Runnable() {@Override
public void run() {try {
myThread01.deNum(t1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
});Thread th1 = new Thread(new Runnable() {
@Override
public void run() {myThread01.adNum(t2);
}
});// 運行
th.start();
th1.start();
}}
t1---a is :4
t2---a is :5
進行阻塞
所以,當同一個對象中的兩個方法有邏輯聯繫的時候,你其中一個加了synchronize,那麼你另一個也應該加上,才能保證返回結果的正確。
鎖對象改變問題
package test19218;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月18日
*/public class TestMyThread02 {
private String tag = "tag";
public TestMyThread02() {
}// 共享變量
private int a = 5;// 方法
public void deNum() throws InterruptedException {synchronized (tag) {
tag = "change tag";System.out
.println(Thread.currentThread().getName() + "sleep begin");
Thread.sleep(6000);
System.out.println(Thread.currentThread().getName() + "sleep over");
}}
// 方法
public synchronized void adNum(String tag) {
a++;
System.out.println(tag + "---a is :" + a);
}public static void main(String[] args) {
final String t1 = "t1";
final String t2 = "t2";// 實列需要多線程的對象
final TestMyThread02 myThread01 = new TestMyThread02();
// final TestMyThread02 myThread02 = new TestMyThread02();// 創建多個線程實列
Thread th = new Thread(new Runnable() {@Override
public void run() {try {
myThread01.deNum();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
}, t1);Thread th1 = new Thread(new Runnable() {
@Override
public void run() {try {
myThread01.deNum();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}}
}, t2);// 運行
th.start();try {
th1.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
th1.start();
}}
th進入鎖以後改變對象,th1立刻就進入,沒有等t1執行完成
如果不改變鎖對象,那麼th1將等th執行完成後才進入。
死鎖問題,
在設計程序時就應該避免雙方相互持有對方的鎖的情況
package com.bjsxt.base.sync006;
/**
* 死鎖問題,在設計程序時就應該避免雙方相互持有對方的鎖的情況
* @author alienware
*
*/
public class DeadLock implements Runnable{private String tag;
private static Object lock1 = new Object();
private static Object lock2 = new Object();
public void setTag(String tag){
this.tag = tag;
}
@Override
public void run() {
if(tag.equals("a")){
synchronized (lock1) {
try {
System.out.println("當前線程 : " + Thread.currentThread().getName() + " 進入lock1執行");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("當前線程 : " + Thread.currentThread().getName() + " 進入lock2執行");
}
}
}
if(tag.equals("b")){
synchronized (lock2) {
try {
System.out.println("當前線程 : " + Thread.currentThread().getName() + " 進入lock2執行");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("當前線程 : " + Thread.currentThread().getName() + " 進入lock1執行");
}
}
}
}
public static void main(String[] args) {
DeadLock d1 = new DeadLock();
d1.setTag("a");
DeadLock d2 = new DeadLock();
d2.setTag("b");
Thread t1 = new Thread(d1, "t1");
Thread t2 = new Thread(d2, "t2");
t1.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
同一對象屬性的修改不會影響鎖的情況
package com.bjsxt.base.sync006;
/**
* 同一對象屬性的修改不會影響鎖的情況
* @author alienware
*
*/
public class ModifyLock {
private String name ;
private int age ;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public synchronized void changeAttributte(String name, int age) {
try {
System.out.println("當前線程 : " + Thread.currentThread().getName() + " 開始");
this.setName(name);
this.setAge(age);
System.out.println("當前線程 : " + Thread.currentThread().getName() + " 修改對象內容爲: "
+ this.getName() + ", " + this.getAge());
Thread.sleep(2000);
System.out.println("當前線程 : " + Thread.currentThread().getName() + " 結束");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
final ModifyLock modifyLock = new ModifyLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
modifyLock.changeAttributte("張三", 20);
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
modifyLock.changeAttributte("李四", 21);
}
},"t2");
t1.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
t2.start();
}
}
使用synchronized代碼塊加鎖,比較靈活
package com.bjsxt.base.sync006;
/**
* 使用synchronized代碼塊加鎖,比較靈活
* @author alienware
*
*/
public class ObjectLock {public void method1(){
synchronized (this) { //對象鎖
try {
System.out.println("do method1..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void method2(){ //類鎖
synchronized (ObjectLock.class) {
try {
System.out.println("do method2..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private Object lock = new Object();
public void method3(){ //任何對象鎖
synchronized (lock) {
try {
System.out.println("do method3..");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final ObjectLock objLock = new ObjectLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method1();
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method2();
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
objLock.method3();
}
});
t1.start();
t2.start();
t3.start();
}
}
synchronized代碼塊對字符串的鎖,注意String常量池的緩存功能
package com.bjsxt.base.sync006;
/**
* synchronized代碼塊對字符串的鎖,注意String常量池的緩存功能
* @author alienware
*
*/
public class StringLock {public void method() {
//new String("字符串常量")
synchronized ("字符串常量") {
try {
while(true){
System.out.println("當前線程 : " + Thread.currentThread().getName() + "開始");
Thread.sleep(1000);
System.out.println("當前線程 : " + Thread.currentThread().getName() + "結束");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final StringLock stringLock = new StringLock();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
stringLock.method();
}
},"t2");
t1.start();
t2.start();
}
}
使用synchronized代碼塊減小鎖的粒度,提高性能
package com.bjsxt.base.sync006;
/**
* 使用synchronized代碼塊減小鎖的粒度,提高性能
* @author alienware
*
*/
public class Optimize {public void doLongTimeTask(){
try {
System.out.println("當前線程開始:" + Thread.currentThread().getName() +
", 正在執行一個較長時間的業務操作,其內容不需要同步");
Thread.sleep(2000);
synchronized(this){
System.out.println("當前線程:" + Thread.currentThread().getName() +
", 執行同步代碼塊,對其同步變量進行操作");
Thread.sleep(1000);
}
System.out.println("當前線程結束:" + Thread.currentThread().getName() +
", 執行完畢");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
final Optimize otz = new Optimize();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
otz.doLongTimeTask();
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
otz.doLongTimeTask();
}
},"t2");
t1.start();
t2.start();
}
}
多個addAndGet在一個方法內是非原子性的,需要加synchronized進行修飾,保證4個addAndGet整體原子性
package other;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月19日
*/public class AtomicTest {
//變量爲atomic系列
private AtomicInteger num = new AtomicInteger(0);
//多次自增 synchronized
public void addNum(){
int i = num.incrementAndGet();
System.out.println("now num is :" + i);
num.incrementAndGet();
num.incrementAndGet();
num.incrementAndGet();
num.incrementAndGet();
}
public static void main(String[] args) {
//實列需要多線程對象
final AtomicTest test = new AtomicTest();
//起多個線程運行
for(int i = 0;i<=100;i++){
//System.out.println("now time is : "+ i);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
test.addNum();
}
});
//啓動
t.start();
}
}
}
結果:
now num is :418
now num is :433
now num is :432
結果是亂的,加上synchronize之後
//多次自增 synchronized
public synchronized void addNum(){
int i = num.incrementAndGet();
System.out.println("now num is :" + i);
num.incrementAndGet();
num.incrementAndGet();
num.incrementAndGet();
num.incrementAndGet();
}
now num is :486
now num is :491
now num is :496
now num is :501
輸出結果總是一直且有序
volatile關鍵字不具備synchronized關鍵字的原子性(同步)
package other;
import java.util.ArrayList;
import java.util.List;import com.mysql.fabric.xmlrpc.base.Array;
/**
* @Description
* @Author zengzhiqiang
* @Date 2019年2月19日
*/public class VolatileTest {
//變量
private volatile int num = 0;
//變量的自增 synchronized
public void addNum(){
num++;
num++;
System.out.println("now num is :" + num);
}
public static void main(String[] args) {
//多線程實列對象
final VolatileTest test = new VolatileTest();
//定義多線程列表
List<Thread> list = new ArrayList<Thread>();
//初始化多個線程
for(int i = 0;i<100;i++){
Thread t = new Thread(new Runnable() {
@Override
public void run() {
test.addNum();
}
});
list.add(t);
}
//啓動多線程
for(Thread th:list){
th.start();
}
}
}
結果
now num is :188
now num is :188
now num is :186
now num is :186
無序,顯示不正確,但是結果爲200,正確
使用synchronize修飾
//變量的自增 synchronized
public synchronized void addNum(){
num++;
num++;
System.out.println("now num is :" + num);
}
結果
now num is :194
now num is :196
now num is :198
now num is :200
有序,顯示正確,且結果正確。