1.並行性
同じオブジェクトが同時に複数のスレッドによって操作されます-チケットの購入、食事の購入、お金の引き出し...
実生活では、解決するためにキューに入れる
キューを使用してプログラムを解決します。
2.キューに入れてロックする
同じプロセス内の複数のスレッドがストレージスペースを共有しているため、アクセスの競合が発生します。!、同期されたロックメカニズムの導入、スレッドがオブジェクト、排他的リソースの排他的ロックを取得すると、他のスレッドは待機し、使用後にロックを解放する必要があります。
問題がある
- パフォーマンスの問題
- 優先度の高いスレッドが優先度の低いスレッドを待機すると、優先度が反転し、パフォーマンスの問題が発生します。
3.例
安全でないチケット購入-競合があります
/**
* 不安全
*/
//多个线程同时操作一个对象
//买火车票——对象车票
//存在线程并发的bug
public class Demo3 implements Runnable{
//票数
int numberTickets = 10;
//线程执行代码
public void run() {
while (numberTickets>0){
//模拟延时
try {
Thread.sleep(200);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了票"+numberTickets--);
}
}
public static void main(String[] args) {
Demo3 demo3 = new Demo3();
//三个人同时抢票
new Thread(demo3,"小明").start();
new Thread(demo3,"小惠").start();
new Thread(demo3,"黄牛党").start();
}
}
対立があります
安全でない撤退
コード
public class BankN {
public static void main(String[] args) {
Account account = new Account(1000000,"小傅的账户");
Drawing you = new Drawing(account,10000,"XiaoFu");
Drawing girlFriend = new Drawing(account,100000,"girlFriend");
you.start();
girlFriend.start();
}
}
class Account{
int money;//余额
String cardName;//卡名
public Account(int money, String cardName) {
this.money = money;
this.cardName = cardName;
}
}
class Drawing extends Thread{
Account account;//账户
int drawing_money;//取了多少钱
int nowMoney;//手里有多少钱
public Drawing(Account account,int drawing_money,String T_name){
super(T_name);
this.account = account;
this.drawing_money = drawing_money;
}
@Override
public void run() {
if (account.money<drawing_money){
System.out.println(Thread.currentThread().getName()+"钱不够取不了");
return;
}
//模拟网络延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money-drawing_money;//账户减少钱
nowMoney = nowMoney+drawing_money;//手里的钱增加
System.out.println(account.cardName+"余额为:"+account.money);
//Thread.currentThread().getName() = this.getName()
System.out.println(this.getName()+"手里的钱:"+nowMoney);
}
}
結果
なぜ対立があるのですか
最後のチケットを例にとると、各プロセスには独自のメモリがあるため、誰もがチケットがあると思います。チケットを購入すると、チケットは0であることがわかります。
つまり、2つのスレッドが同じデータを同時に操作します。結果は10000ではありません
/**
* 线程是不安全的
*/
public class ThreadUn {
public static void main(String[] args) throws InterruptedException {
List<Thread> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread());
}).start();
}
Thread.sleep(3000);
System.out.println(list.size());
}
}
4.実装メカニズム
改善後-ロックを介して競合を解決します(同期されたキーワード)
- シンクロナイズド
- 同期块
すべてのオブジェクトにはロックがあります
欠陥:大きなメソッドを同期済みとして宣言するのは非効率的です。
メソッドで変更する必要があるコンテンツはロックする必要があります。
1)同期方法
//synchronized 同步方法——锁的是this
private synchronized void buy() throws InterruptedException {
if (numberTickets<=0){
flag = false;
return;
}
//模拟网络延时
Thread.sleep(200);
//模拟买票
System.out.println(Thread.currentThread().getName()+"拿到了票"+numberTickets--);
}
2)同期块
ロックの対象は、追加、削除、変更、および検査の対象である必要があります-変更の量
デフォルトのロックはこれです-したがって、runメソッドの代わりにアカウントをロックする必要があります。
// synchronized默认锁的是this
public void run() {
// synchronized块
synchronized (account){
if (account.money<drawing_money){
System.out.println(Thread.currentThread().getName()+"钱不够取不了");
return;
}
//模拟网络延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money-drawing_money;//账户减少钱
nowMoney = nowMoney+drawing_money;//手里的钱增加
System.out.println(account.cardName+"余额为:"+account.money);
//Thread.currentThread().getName() = this.getName()
System.out.println(this.getName()+"手里的钱:"+nowMoney);
}
}
runメソッド本体に同期ブロックを追加します
public static void main(String[] args) throws InterruptedException {
List<Thread> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized (list){
list.add(Thread.currentThread());
}
}).start();
}
Thread.sleep(1000);
System.out.println(list.size());
}
3)java並行パッケージ-並行リンクリスト
public class CopyOnWriteArrayList<E>
extends Object
implements List<E>, RandomAccess, Cloneable, Serializable
すべての変更で、スレッドセーフな変数ArrayList
(、 add
、set
など)元の配列の新バージョンによって実現されます。
これは通常はコストがかかりますが、トラバーサル操作がミューテーションを大幅に超える場合に選択するよりも効率的です。トラバーサルを同期できない、または同期したくないが、並行スレッド間の干渉を排除する必要がある場合に役立ちます。「スナップショット」スタイルのイテレータメソッドは、イテレータを作成するときに配列の状態への参照を使用します。このアレイではイテレータの寿命が変更されていないため、干渉は不可能であり、イテレータが失われないことが保証されていますConcurrentModificationException
。イテレータは、イテレータが作成されてからのリストへの追加、削除、または変更を反映しません。人民元は、反復子自体の動作を変更(remove
、、set
およびadd
)をサポートしていません。これらのメソッドは置きUnsupportedOperationException
ます。
を含むすべての要素が許可されnull
ます。
メモリ整合性効果:他の同時コレクションでは、オブジェクトをCopyOnWriteArrayList
発生前アクションに配置するときの、別のスレッドCopyOnWriteArrayList
へのアクセスおよび要素の削除前のスレッドの動作。
//java实现的线程安全的链表
public class TestJUC {
public static void main(String[] args) {
CopyOnWriteArrayList<String> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
for (int i = 0; i < 1000; i++) {
new Thread(()->{
copyOnWriteArrayList.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(copyOnWriteArrayList.size());
}
}
5.デッドロックの問題
スレッドのグループは、互いの必要なリソース(ロック)を保持しており、それらを解放することはできません。
synchronized (Object){
//代码体
}
同期(オブジェクト)は、オブジェクトをロックすることと同じです。
コード本体が実行された後は、ロックを解除するのと同じです。
例
public class TestDeadLock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0,"灰姑娘");
Makeup g2 = new Makeup(1,"白雪公主");
g1.start();
g2.start();
}
}
//口红
class Lipstick{
}
//镜子
class Mirror{
}
class Makeup extends Thread{
//需要的资源,用static表示只有一份资源
static Lipstick lipstick = new Lipstick();
static Mirror mirror = new Mirror();
int choice;
String girlName;
public Makeup(int choice,String girlName){
this.choice = choice;
this.girlName = girlName;
}
//线程运行主体
public void run() {
//化妆
make_up();
}
//互相持有对方的资源
private void make_up(){
if (choice==0){
//获得口红的锁
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
//欲获得镜子
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
}
}
}
if (choice==1){
//获得口红的锁
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//欲获得镜子
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
}
問題を解決します-ロックを取得した後に解放します。
//互相持有对方的资源
private void make_up(){
if (choice==0){
//获得口红的锁
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁");
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//欲获得镜子
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
}
}
if (choice==1){
//获得镜子的锁
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//欲获得口红
synchronized (lipstick){
System.out.println(this.girlName+"获得口红的锁");
}
}
}
要約:デッドロックの必要条件