マルチスレッド-スレッド同期(5)
-
マルチスレッドの形成条件:キュー+ロック(安全のため)
-
スレッドの同期は安全です。まず、スレッドの不安定さを見てみましょう。
-
スレッドの不安定さの一例:
//不安全的买票 //线程不安全,有负数 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket station=new BuyTicket(); new Thread(station,"苦逼的我").start(); new Thread(station,"牛逼的你们").start(); new Thread(station,"可恶的黄牛党").start(); } } class BuyTicket implements Runnable{ //票 private int ticketNums=10; boolean flag=true;//外部停止方式 @Override public void run() { //买票 while (flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } private void buy() throws InterruptedException { //判断是否有票 if (ticketNums<=0){ flag =false; return; } //模拟延时 Thread.sleep(100); //买票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }
-
スレッドの不安定さの最初の例、-1が表示される状況の概略図:
-
-
スレッドの不安定さの2番目の例:
//不安全的取钱 //两个人去银行取钱,账户 public class UnsafeBank { public static void main(String[] args) { //账户 Account account=new Account(100,"结婚基金"); Drawing you=new Drawing(account,50,"你"); Drawing girlFriend=new Drawing(account,100,"girlFriend"); you.start(); girlFriend.start(); } } //账户 class Account{ int money;//余额 String name;//卡名 public Account(int money,String name){ this.money=money; this.name=name; } } //银行:模拟取款 class Drawing extends Thread{ Account account;//账户 //取了多少钱 int drawingMoney; //现在手里有多少钱 int nowMoney; public Drawing(Account account, int drawingMoney,String name){ super(name);//调用父类的有参构造 this.account=account; this.drawingMoney=drawingMoney; } //取钱 @Override public void run() { //判断有没有钱 if (account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } //sleep可以放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额= 余额-你取的钱 account.money = account.money-drawingMoney; //你手里的钱 nowMoney = nowMoney+drawingMoney; System.out.println(account.name+"余额为:"+account.money); //Thread.currentThread().getName()=this.getName() //获取线程名字 System.out.println(this.getName()+"手里的钱:"+nowMoney); } }
-
スレッドの不安定さの2番目の例、-50のバランスがある状況の概略図:
-
スレッドの不安定さの3つの例:
import java.util.ArrayList; import java.util.List; //线程不安全的集合 public class UnsafeList { public static void main(String[] args) { List<String> list=new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ //不安全的原因:因为2个线程同一个瞬间操作了同一个位置,把两个数组添加了同一个位置就把它覆盖掉了,覆盖掉了,元素就会少,少的元素就是这么来的 list.add(Thread.currentThread().getName()); }).start(); } try { Thread.sleep(3000);//让他睡3秒,放大问题的发生性,还是打印不到1w个线程 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size());//线程的大小没能够按照理论的打印1w个线程 } }
-
スレッドの不安定さの理由:
各スレッドは、独自の作業メモリー内で相互作用します。不適切なメモリー制御は、データの不整合を引き起こします。率直に言って、スレッドのメモリはそれ自身のものであり、実際、各スレッドはシステムのメモリを持ち、メモリは負になり、スレッドの不安定さにもつながります。
-
スレッドの不安定さの問題を解決するには、次のようにします。同期方法を使用する
-
このメソッドのメカニズムのセットを提案します。このメカニズムのセットは同期キーワードであり、同期メソッドと同期ブロックの2つのメソッドが含まれています。
同期メソッド:public synchronized void method(int args ){}
-
同期されたメソッドは、「オブジェクト」へのアクセスを制御します。各オブジェクトはロックに対応します。同期された各メソッドは、実行するメソッドのオブジェクトのロックを呼び出す必要があります。そうしないと、スレッドがブロックされます。メソッドが実行されると、ロックはロックされます。メソッドが戻ってロックを解放し、ブロックされたスレッドがロックを取得して実行を続行できるようになるまで、排他的に使用されます
欠陥:大きなメソッドが同期されていると宣言されている場合、効率に影響します
-
- 同期されたブロック:synchronized(Obj){}
- Objはそれを同期モニターと呼んでいます
- Objは任意のオブジェクトにすることができますが、同期モニターとして共有リソースを使用することをお勧めします
- 同期メソッドの同期モニターは、オブジェクト自体またはクラス[リフレクションで説明]であるthisであるため、同期メソッドで同期モニターを指定する必要はありません。
- 同期モニターの実行プロセス:同期メソッドの原理は同じです
-
スレッドの不安定さを解決する最初の例は、synchronizedキーワードをbuy()メソッドに追加して、同期同期メソッドに変換することです。
//不安全的买票 //线程不安全,有负数 public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket station=new BuyTicket(); new Thread(station,"苦逼的我").start(); new Thread(station,"牛逼的你们").start(); new Thread(station,"可恶的黄牛党").start(); } } class BuyTicket implements Runnable{ //票 private int ticketNums=10; boolean flag=true;//外部停止方式 @Override public void run() { //买票 while (flag){ try { buy(); } catch (InterruptedException e) { e.printStackTrace(); } } } //synchronized 同步方法,锁的是this private synchronized void buy() throws InterruptedException { //判断是否有票 if (ticketNums<=0){ flag =false; return; } //模拟延时 Thread.sleep(100); //买票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--); } }
-
スレッドの不安定さを解決する2番目の例は、同期されたアカウントのブロックをロックすることによってのみ解決できます。
//不安全的取钱 //两个人去银行取钱,账户 public class UnsafeBank { public static void main(String[] args) { //账户 Account account=new Account(100,"结婚基金"); Drawing you=new Drawing(account,50,"你"); Drawing girlFriend=new Drawing(account,100,"girlFriend"); you.start(); girlFriend.start(); } } //账户 class Account{ int money;//余额 String name;//卡名 public Account(int money,String name){ this.money=money; this.name=name; } } //银行:模拟取款 class Drawing extends Thread{ Account account;//账户 //取了多少钱 int drawingMoney; //现在手里有多少钱 int nowMoney; public Drawing(Account account, int drawingMoney,String name){ super(name);//调用父类的有参构造 this.account=account; this.drawingMoney=drawingMoney; } //取钱 //synchronized 默认锁的是this @Override public void run() { synchronized (account){ //这里只有锁的是account的同步块才有用,锁run()方法是没用的 //判断有没有钱 if (account.money-drawingMoney<0){ System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } //sleep可以放大问题的发生性 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } //卡内余额= 余额-你取的钱 account.money = account.money-drawingMoney; //你手里的钱 nowMoney = nowMoney+drawingMoney; System.out.println(account.name+"余额为:"+account.money); //Thread.currentThread().getName()=this.getName() //获取线程名字 System.out.println(this.getName()+"手里的钱:"+nowMoney); } } }
-
スレッドの不安定さを解決する3番目の例は、同期されたブロックロックリストを使用することです。
import java.util.ArrayList; import java.util.List; //线程不安全的集合 public class UnsafeList { public static void main(String[] args) { List<String> list=new ArrayList<>(); for (int i = 0; i < 10000; i++) { new Thread(()->{ //不安全的原因:因为2个线程同一个瞬间操作了同一个位置,把两个数组添加了同一个位置就把它覆盖掉了,覆盖掉了,元素就会少,少的元素就是这么来的 synchronized (list){ list.add(Thread.currentThread().getName()); } }).start(); } try { Thread.sleep(3000);//让他睡3秒,放大问题的发生性,还是打印不到1w个线程 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(list.size());//线程的大小没能够按照理论的打印1w个线程 } }
-
補足:JUC安全タイプのコレクションは、いくつかのスレッドセーフの問題も解決できます。
import java.util.concurrent.CopyOnWriteArrayList;
//测试JUC安全类型的集合
public class TestJUC {
public static void main(String[] args) {
//concurrent并发包下写好的,安全的ArrayList
CopyOnWriteArrayList<String> list=new CopyOnWriteArrayList<String>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}