多執行緒學習-----------執行緒之間的通訊(三)
阿新 • • 發佈:2018-11-03
執行緒安全問題(demo:銀行轉賬):多個執行緒操作相同一個數據的時候就會出現執行緒安全問題。
程式舉例:
public class SynchronizedStudy { class Output{ public void printName(String name){ for(int i = 0;i < name.length();i++){ System.out.print(name.charAt(i)); } System.out.println(); } } private void init(){ final Output o = new Output(); //啟動兩個執行緒 new Thread(new Runnable() { @Override public void run() { while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } o.printName("wangjinglong"); } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } o.printName("123456"); } } }).start(); } public static void main(String[] args) { new SynchronizedStudy().init(); } }
執行結果:
出現執行緒安全問題
使用synchronize(使用同一個物件才能達到互斥的效果)
修改以上程式碼
class Output{
public void printName(String name){
synchronized (name) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
這樣仍然存線上程問題,因為name不是同一個物件。
再次對原來的程式碼修改
class Output{
public void printName(String name){
String s = "xx";
synchronized (s) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
使得對同一個物件進行互斥,執行緒安全問題不再出現。
再次對原來的程式碼修改
private void init(){ final Output o = new Output(); //啟動兩個執行緒 new Thread(new Runnable() { @Override public void run() { while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //修改以前o.printName("wangjinglong"); //修改以後 new Output().printName("wangjinglong"); } } }).start(); new Thread(new Runnable() { @Override public void run() { while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //修改以前o.printName("123456"); //修改以後 new Output().printName("123456"); } } }).start(); }
又會產生執行緒安全問題,因為不是同一個物件。
再次對原來的程式碼修改
class Output{
public void printName(String name){
synchronized (this) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
執行緒安全問題解除,因為this是指當前呼叫它的物件,而兩個執行緒中是用的Output的同一個物件,所以實現了互斥訪問。
等價於這樣寫
class Output{
public synchronized void printName(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
一個方法中對同一個物件的限制只能用一個synchronize,否則會發送死鎖,如以下所示:
class Output{
public synchronized void printName(String name){
synchronized (this) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}
這樣依然能實現互斥(兩個執行緒一個呼叫printName,另一個呼叫printName1):
class Output{
public synchronized void printName(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
public synchronized void printName1(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
再次修改(兩個執行緒一個呼叫printName,另一個呼叫printName3):
static class Output{
public synchronized void printName(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
public synchronized void printName1(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
public static synchronized void printName3(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
這樣修改,又會出現執行緒安全問題。
解決問題(如下所示):
static class Output{
public void printName(String name){
synchronized (Output.class) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
public synchronized void printName1(String name){
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
public static void printName3(String name){
synchronized (Output.class) {
for(int i = 0;i < name.length();i++){
System.out.print(name.charAt(i));
}
System.out.println();
}
}
}