1.线程同步

1.1 存在线程安全问题的代码

 1 package com.example.concurrency;
 2 
 3 import java.util.Arrays;
 4 
 5 public class demo09 {
 6 
 7     private static int index = 0;
 8 
 9     public static void main(String[] args) throws Exception {
10         String[] s = new String[5];
11 
12         Runnable ra = new Runnable() {
13             @Override
14             public void run() {
15                 s[index] = "hello";
16                 index++;
17             }
18         };
19 
20         Runnable rb = new Runnable() {
21 
22             @Override
23             public void run() {
24                 s[index] = "world";
25                 index++;
26 
27             }
28         };
29 
30         Thread a = new Thread(ra, "a");
31         Thread b = new Thread(rb, "b");
32         a.start();
33         b.start();
34 
35         a.join();
36         b.join();
37         
38         System.out.println(Arrays.toString(s));
39     }
40 }

反复执行,有可能得到如下的结果:

 

 

1.2 使用同步代码块

 1 package com.example.concurrency;
 2 
 3 import java.util.Arrays;
 4 
 5 public class demo09 {
 6 
 7     private static int index = 0;
 8 
 9     public static void main(String[] args) throws Exception {
10         String[] s = new String[5];
11 
12         Runnable ra = new Runnable() {
13             @Override
14             public void run() {
15                 synchronized (s) {
16                     s[index] = "hello";
17                     index++;
18                 }
19             }
20         };
21 
22         Runnable rb = new Runnable() {
23 
24             @Override
25             public void run() {
26                 synchronized (s) {
27                     s[index] = "world";
28                     index++;
29                 }
30             }
31         };
32 
33         Thread a = new Thread(ra, "a");
34         Thread b = new Thread(rb, "b");
35         a.start();
36         b.start();
37 
38         a.join();
39         b.join();
40 
41         System.out.println(Arrays.toString(s));
42     }
43 }

注意第15行和26行,增加了同步关键字synchronized,在同步代码块中,临界资源s被锁住,保证同时只能有一个线程执行这段代码。

 

1.3使用同步方法,解决买票问题

 1 package com.example.concurrency;
 2 
 3 public class MyTicket implements Runnable {
 4     
 5     private int ticket = 100;
 6 
 7     @Override
 8     public void run() {
 9         while (true) {
10             if (!sale()) {
11                 break;
12             }
13         }
14     }
15 
16     private synchronized boolean sale() {
17         if (ticket <= 0) {
18             return false;
19         }
20         System.out.println(Thread.currentThread().getName() + "卖了第" + ticket + "张票");
21         ticket--;
22         return true;
23     }
24 }
 1 package com.example.concurrency;
 2 
 3 public class TestTicket {
 4     public static void main(String[] args) {
 5         // TODO Auto-generated method stub
 6         MyTicket ticket = new MyTicket();
 7         Thread t1 = new Thread(ticket, "win1");
 8         Thread t2 = new Thread(ticket, "win2");
 9         Thread t3 = new Thread(ticket, "win3");
10         Thread t4 = new Thread(ticket, "win4");
11 
12         t1.start();
13         t2.start();
14         t3.start();
15         t4.start();
16     }
17 }

使用同步方法,就不会再将同一张票重复销售了。

每个对象都有一个互斥锁标记,用来分配给线程。

只有拥有对象互斥锁标记的线程,才能进入对该新对象加锁的同步代码块。

线程退出同步代码块时,会释放相应的互斥锁标记。

 

1.4存钱取钱问题

 1 package com.example.concurrency;
 2 
 3 public class BankCard {
 4     private double money;
 5 
 6     public double getMoney() {
 7         return money;
 8     }
 9 
10     public void setMoney(double money) {
11         this.money = money;
12     }
13 
14     public void add(double money) {
15         this.money += money;
16     }
17 
18     public void sub(double money) {
19         this.money -= money;
20     }
21 }
 1 package com.example.concurrency;
 2 
 3 public class TestBankCard {
 4 
 5     public static void main(String[] args) {
 6         // TODO Auto-generated method stub
 7         BankCard card = new BankCard();
 8 
 9         Runnable add = new Runnable() {
10 
11             @Override
12             public void run() {
13                 for (int i = 0; i < 10; i++) {
14                     synchronized (card) {
15                         card.add(100);
16                         System.out.println("存入100元,当前余额为:" + card.getMoney());
17                     }
18                 }
19 
20             }
21         };
22 
23         Runnable sub = new Runnable() {
24             @Override
25             public void run() {
26                 // TODO Auto-generated method stub
27                 for (int i = 0; i < 10; i++) {
28                     synchronized (card) {
29                         if (card.getMoney() >= 100) {
30                             card.sub(100);
31                             System.out.println("取出100元,当前余额为:" + card.getMoney());
32                         } else {
33                             System.out.println("余额不足");
34                             i--;
35                         }
36                     }
37                 }
38             }
39         };
40 
41         new Thread(add).start();
42         new Thread(sub).start();
43     }
44 }

同步规则:

只有再调用包含同步代码块的方法,或者同步方法时,才需要对象的锁标记。

如调用不包含同步代码得方法,或者普通方法时,则不需要锁标记,可直接使用。

JDK中实现的同步数据结构有:StringBuffer、Vector、Hashtable。

 

1.5使用Lock进行线程同步

 1 public class demo04 {
 2     //演示lock的使用
 3     public static void main(String[] args) throws InterruptedException {
 4         demo04 d04 = new demo04();
 5         MyList myList = d04.new MyList();
 6         
 7         Runnable runnable1 = new Runnable() {            
 8             @Override
 9             public void run() {
10                 myList.add("hello");
11             }
12         };
13         
14         Runnable runnable2 = new Runnable() {            
15             @Override
16             public void run() {
17                 myList.add("world");
18             }
19         };
20         
21         Thread thread1 = new Thread(runnable1,"线程1");
22         Thread thread2 = new Thread(runnable2,"线程2");
23         
24         thread1.start();
25         thread2.start();
26         
27         thread1.join();
28         thread1.join();
29         
30         System.out.println(Arrays.toString(myList.getStr()));
31     }
32     
33     
34     class MyList {
35         private Lock lock = new ReentrantLock();
36         private String[] str = new String[] {"A","B","","",""};
37         private int count = 2;
38         
39         public String[] getStr() {
40             return str;
41         }
42 
43         public void setStr(String[] str) {
44             this.str = str;
45         }
46 
47         public int getCount() {
48             return count;
49         }
50 
51         public void setCount(int count) {
52             this.count = count;
53         }
54 
55         public void add(String value) {
56             lock.lock();
57             try {
58                 str[count]=value;
59                 count++;
60                 System.out.println(Thread.currentThread().getName()+"添加了数据:"+value);
61             }
62             finally {
63                 lock.unlock();
64             }            
65         }
66     }
67 }

可能的执行结果:

线程1添加了数据:hello
线程2添加了数据:world
[A, B, hello, world, ]

 

1.6使用读写锁代码示例:

 1 public class demo05 {
 2     public static void main(String[] args) {
 3         demo05 d05 = new demo05();
 4         MyClass myClass = d05.new MyClass();
 5         Runnable runnable1 = new Runnable() {
 6             
 7             @Override
 8             public void run() {
 9                 // TODO Auto-generated method stub
10                 try {
11                     myClass.SetValue(1);
12                 } catch (Exception e) {
13                     // TODO Auto-generated catch block
14                     e.printStackTrace();
15                 }
16             }
17         };
18         
19         Runnable runnable2 = new Runnable() {
20             
21             @Override
22             public void run() {
23                 // TODO Auto-generated method stub
24                 try {
25                     myClass.getValue();
26                 } catch (Exception e) {
27                     // TODO Auto-generated catch block
28                     e.printStackTrace();
29                 }
30             }
31         };
32         
33         ExecutorService executorService = Executors.newFixedThreadPool(20);
34         long startTime = System.currentTimeMillis();
35         
36         for(int i=0;i<2;i++) {
37             executorService.submit(runnable1);
38         }
39         for(int i=0;i<18;i++) {
40             executorService.submit(runnable2);
41         }
42         executorService.shutdown();
43         while(!executorService.isTerminated()) {
44             
45         }
46         System.out.println("共用时间:"+(System.currentTimeMillis()-startTime)+"毫秒");
47     }
48     
49     
50     class MyClass{
51         ReentrantReadWriteLock rwlLock = new ReentrantReadWriteLock();
52         ReadLock readLock = rwlLock.readLock();
53         WriteLock writeLock = rwlLock.writeLock();
54         
55         private int value;
56         
57         public int getValue() throws Exception {
58             readLock.lock();
59             try {
60                 Thread.sleep(1000);
61                 return value;
62             } finally {
63                 // TODO: handle finally clause
64                 readLock.unlock();
65             }
66         }
67         
68         public void SetValue(int value) throws Exception {
69             writeLock.lock();
70             try {
71                 Thread.sleep(1000);
72                 this.value=value;
73             } finally {
74                 // TODO: handle finally clause
75                 writeLock.unlock();
76             }
77         }
78     }
79 }

运行结果:

共用时间:3025毫秒

可以看到,使用读写锁耗时大概3秒左右。

而如果使用的是ReentrantLock,大概需要20秒左右时间。

 

2.线程死锁

2.1存在死锁问题的代码演示:

1 package com.example.concurrency;
2 
3 public class MyLock {
4     public static Object a = new Object();
5     public static Object b = new Object();    
6 }
 1 package com.example.concurrency;
 2 
 3 public class Boy extends Thread {    
 4     @Override
 5     public void run() {        
 6         synchronized (MyLock.a) {
 7             System.out.println("boy拿到了a");
 8             synchronized (MyLock.b) {
 9                 System.out.println("boy拿到了b");
10                 System.out.println("boy获取了全部资源");    
11             }            
12         }
13     }
14 }
 1 package com.example.concurrency;
 2 
 3 public class Girl extends Thread {
 4     @Override
 5     public void run() {
 6         
 7         synchronized (MyLock.b) {
 8             System.out.println("girl拿到了b");
 9             synchronized (MyLock.a) {
10                 System.out.println("girl拿到了a");
11                 System.out.println("girl获取了全部资源");    
12             }            
13         }
14     }
15 }
 1 package com.example.concurrency;
 2 
 3 public class TestEat {
 4     public static void main(String[] args) {
 5         // TODO Auto-generated method stub
 6         Boy boy = new Boy();
 7         Girl girl = new Girl();
 8         
 9         boy.start();
10         girl.start();
11     }
12 }

执行TestEat中的main方法,有可能出现死锁。boy线程和girl线程,都在等待对方释放锁,但由于自己没有释放锁,对方也不能释放锁。

 

2.2使用线程通信,避免出现死锁

 1 package com.example.concurrency;
 2 
 3 public class BankCard {
 4     private double money;
 5     
 6     private boolean flag = false;//标记当前是否有钱,true有钱,false没钱
 7 
 8     public double getMoney() {
 9         return money;
10     }
11 
12     public void setMoney(double money) {
13         this.money = money;
14     }
15 
16     public synchronized void add(double money) throws Exception {
17         while (flag) {
18             this.wait();
19 //            System.out.println("现在想存钱,但是账户里有钱,等待先取走");
20         }
21 
22         this.money += money;
23         this.flag = true;
24         System.out.println("存入100元,账户余额:" + this.money);
25         this.notifyAll();// 通知其他线程,可以获取锁
26     }
27 
28     public synchronized void sub(double money) throws Exception {
29         while (!flag) {
30             this.wait();
31 //            System.out.println("现在想取钱,但是账户没有钱,等待先存入");
32         }
33 
34         this.money -= money;
35         this.flag = false;
36         System.out.println("取出100元,账户余额:" + this.money);
37         this.notifyAll();
38     }
39 }
 1 package com.example.concurrency;
 2 
 3 public class TestBankCard {
 4 
 5     public static void main(String[] args) {
 6         // TODO Auto-generated method stub
 7         BankCard card = new BankCard();
 8 
 9         Runnable add = new Runnable() {
10 
11             @Override
12             public void run() {
13                 for (int i = 0; i < 10; i++) {
14                     try {
15                         card.add(100);
16                     } catch (Exception e) {
17                         // TODO Auto-generated catch block
18                         e.printStackTrace();
19                     }
20                 }
21 
22             }
23         };
24 
25         Runnable sub = new Runnable() {
26             @Override
27             public void run() {
28                 // TODO Auto-generated method stub
29                 for (int i = 0; i < 10; i++) {
30                     try {
31                         card.sub(100);
32                     } catch (Exception e) {
33                         // TODO Auto-generated catch block
34                         e.printStackTrace();
35                     }
36                 }
37             }
38         };
39 
40         new Thread(add).start();
41         new Thread(sub).start();
42     }
43 }

执行结果如下:

 1 存入100元,账户余额:100.0
 2 取出100元,账户余额:0.0
 3 存入100元,账户余额:100.0
 4 取出100元,账户余额:0.0
 5 存入100元,账户余额:100.0
 6 取出100元,账户余额:0.0
 7 存入100元,账户余额:100.0
 8 取出100元,账户余额:0.0
 9 存入100元,账户余额:100.0
10 取出100元,账户余额:0.0
11 存入100元,账户余额:100.0
12 取出100元,账户余额:0.0
13 存入100元,账户余额:100.0
14 取出100元,账户余额:0.0
15 存入100元,账户余额:100.0
16 取出100元,账户余额:0.0
17 存入100元,账户余额:100.0
18 取出100元,账户余额:0.0
19 存入100元,账户余额:100.0
20 取出100元,账户余额:0.0

使用wait()和notifyAll()实现了线程的通信,完成“一存一取,交叉运行”的效果。

 

3.生产者消费者问题

 1 package com.example.concurrency;
 2 
 3 public class Bread {
 4     private Integer id;
 5     private String name;    
 6     
 7     public Integer getId() {
 8         return id;
 9     }
10     public void setId(Integer id) {
11         this.id = id;
12     }
13     public String getName() {
14         return name;
15     }
16     public void setName(String name) {
17         this.name = name;
18     }    
19     
20     public Bread() {
21         super();
22         // TODO Auto-generated constructor stub
23     }
24     public Bread(Integer id, String name) {
25         super();
26         this.id = id;
27         this.name = name;
28     }    
29 }
 1 package com.example.concurrency;
 2 
 3 public class BreadFactory {
 4     private Bread[] factory = new Bread[10];
 5 
 6     int index = 0;
 7 
 8     public synchronized void product(Bread b) {
 9         while (index >= 5) {
10             try {
11                 this.wait();
12             } catch (InterruptedException e) {
13                 // TODO Auto-generated catch block
14                 e.printStackTrace();
15             }
16         }
17 
18         factory[index] = b;
19         index++;
20         System.out.println(Thread.currentThread().getName() + "生产了1个面包,库存还有" + index + "个");
21         this.notifyAll();
22     }
23 
24     public synchronized Bread sale() {
25         while (index <= 0) {
26             try {
27                 this.wait();
28             } catch (InterruptedException e) {
29                 // TODO Auto-generated catch block
30                 e.printStackTrace();
31             }
32         }
33         index--;
34         Bread b = factory[index];
35         factory[index] = null;
36         System.out.println(Thread.currentThread().getName() + "销售了1个面包,库存还有" + index + "个");
37         this.notifyAll();
38         return b;
39     }
40 }
 1 package com.example.concurrency;
 2 
 3 public class BreadProd implements Runnable {
 4     private BreadFactory factory = new BreadFactory();
 5 
 6     private String name;
 7 
 8     public String getName() {
 9         return name;
10     }
11 
12     public void setName(String name) {
13         this.name = name;
14     }        
15 
16     public BreadProd() {
17         super();
18         // TODO Auto-generated constructor stub
19     }
20 
21     public BreadProd(BreadFactory factory, String name) {
22         super();
23         this.factory = factory;
24         this.name = name;
25     }
26 
27     @Override
28     public void run() {
29         // TODO Auto-generated method stub
30         for (int i = 0; i < 20; i++) {
31             factory.product(new Bread(i, this.name));
32         }
33     }
34 }
 1 package com.example.concurrency;
 2 
 3 public class BreadSaler implements Runnable {
 4     private BreadFactory factory = new BreadFactory();
 5 
 6     private String name;
 7 
 8     public String getName() {
 9         return name;
10     }
11 
12     public void setName(String name) {
13         this.name = name;
14     }    
15 
16     public BreadSaler() {
17         super();
18         // TODO Auto-generated constructor stub
19     }
20 
21     public BreadSaler(BreadFactory factory, String name) {
22         super();
23         this.factory = factory;
24         this.name = name;
25     }
26 
27     @Override
28     public void run() {
29         // TODO Auto-generated method stub
30         for (int i = 0; i < 20; i++) {
31             factory.sale();
32         }
33     }
34 }
 1 package com.example.concurrency;
 2 
 3 public class BreadTest {
 4     public static void main(String[] args) {
 5         BreadFactory factory = new BreadFactory();
 6         BreadProd prod = new BreadProd(factory, "顾客");
 7         BreadSaler saler = new BreadSaler(factory, "面包店");
 8 
 9         new Thread(prod).start();
10         new Thread(saler).start();            
11     }
12 }

执行结果可能为:

 1 Thread-0生产了1个面包,库存还有1个
 2 Thread-0生产了1个面包,库存还有2个
 3 Thread-0生产了1个面包,库存还有3个
 4 Thread-0生产了1个面包,库存还有4个
 5 Thread-0生产了1个面包,库存还有5个
 6 Thread-1销售了1个面包,库存还有4个
 7 Thread-1销售了1个面包,库存还有3个
 8 Thread-1销售了1个面包,库存还有2个
 9 Thread-1销售了1个面包,库存还有1个
10 Thread-1销售了1个面包,库存还有0个
11 Thread-0生产了1个面包,库存还有1个
12 Thread-0生产了1个面包,库存还有2个
13 Thread-0生产了1个面包,库存还有3个
14 Thread-0生产了1个面包,库存还有4个
15 Thread-0生产了1个面包,库存还有5个
16 Thread-1销售了1个面包,库存还有4个
17 Thread-1销售了1个面包,库存还有3个
18 Thread-1销售了1个面包,库存还有2个
19 Thread-1销售了1个面包,库存还有1个
20 Thread-1销售了1个面包,库存还有0个
21 Thread-0生产了1个面包,库存还有1个
22 Thread-0生产了1个面包,库存还有2个
23 Thread-0生产了1个面包,库存还有3个
24 Thread-0生产了1个面包,库存还有4个
25 Thread-0生产了1个面包,库存还有5个
26 Thread-1销售了1个面包,库存还有4个
27 Thread-1销售了1个面包,库存还有3个
28 Thread-1销售了1个面包,库存还有2个
29 Thread-1销售了1个面包,库存还有1个
30 Thread-1销售了1个面包,库存还有0个
31 Thread-0生产了1个面包,库存还有1个
32 Thread-0生产了1个面包,库存还有2个
33 Thread-0生产了1个面包,库存还有3个
34 Thread-0生产了1个面包,库存还有4个
35 Thread-0生产了1个面包,库存还有5个
36 Thread-1销售了1个面包,库存还有4个
37 Thread-1销售了1个面包,库存还有3个
38 Thread-1销售了1个面包,库存还有2个
39 Thread-1销售了1个面包,库存还有1个
40 Thread-1销售了1个面包,库存还有0个

生产的产品数量最多为5个,消费后的产品数量不会小于0个。 

posted on 2021-02-04 16:13  Sempron2800+  阅读(76)  评论(0编辑  收藏  举报