Java笔记(24):多线程(02)

Posted 花醉红尘

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java笔记(24):多线程(02)相关的知识,希望对你有一定的参考价值。

1、JDK5之后的Lock锁的概述和使用

 1 package cn.itcast_01;
 2 
 3 import java.util.concurrent.locks.Lock;
 4 import java.util.concurrent.locks.ReentrantLock;
 5 
 6 public class SellTicket implements Runnable {
 7 
 8     // 定义票
 9     private int tickets = 100;
10 
11     // 定义锁对象
12     private Lock lock = new ReentrantLock();
13 
14     @Override
15     public void run() {
16         while (true) {
17             try {
18                 // 加锁
19                 lock.lock();
20                 if (tickets > 0) {
21                     try {
22                         Thread.sleep(100);
23                     } catch (InterruptedException e) {
24                         e.printStackTrace();
25                     }
26                     System.out.println(Thread.currentThread().getName()
27                             + "正在出售第" + (tickets--) + "张票");
28                 }
29             } finally {
30                 // 释放锁
31                 lock.unlock();
32             }
33         }
34     }
35 
36 }
 1 package cn.itcast_01;
 2 /*
 3  * 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,
 4  * 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock。
 5  * 
 6  * Lock:
 7  *         void lock(): 获取锁。
 8  *         void unlock():释放锁。  
 9  * ReentrantLock是Lock的实现类.
10  */
11 public class SellTicketDemo {
12     public static void main(String[] args) {
13         // 创建资源对象
14         SellTicket st = new SellTicket();
15 
16         // 创建三个窗口
17         Thread t1 = new Thread(st, "窗口1");
18         Thread t2 = new Thread(st, "窗口2");
19         Thread t3 = new Thread(st, "窗口3");
20 
21         // 启动线程
22         t1.start();
23         t2.start();
24         t3.start();
25     }
26 }

2、死锁问题概述和使用

 1 package cn.itcast_02;
 2 
 3 public class DieLock extends Thread {
 4 
 5     private boolean flag;
 6 
 7     public DieLock(boolean flag) {
 8         this.flag = flag;
 9     }
10 
11     @Override
12     public void run() {
13         if (flag) {
14             synchronized (MyLock.objA) {
15                 System.out.println("if objA");
16                 synchronized (MyLock.objB) {
17                     System.out.println("if objB");
18                 }
19             }
20         } else {
21             synchronized (MyLock.objB) {
22                 System.out.println("else objB");
23                 synchronized (MyLock.objA) {
24                     System.out.println("else objA");
25                 }
26             }
27         }
28     }
29 }
 1 package cn.itcast_02;
 2 
 3 /*
 4  * 同步的弊端:
 5  *         A:效率低
 6  *         B:容易产生死锁
 7  * 
 8  * 死锁:
 9  *         两个或两个以上的线程在争夺资源的过程中,发生的一种相互等待的现象。
10  * 
11  * 举例:
12  *         中国人,美国人吃饭案例。
13  *         正常情况:
14  *             中国人:筷子两支
15  *             美国人:刀和叉
16  *         现在:
17  *             中国人:筷子1支,刀一把
18  *             美国人:筷子1支,叉一把
19  */
20 public class DieLockDemo {
21     public static void main(String[] args) {
22         DieLock dl1 = new DieLock(true);
23         DieLock dl2 = new DieLock(false);
24 
25         dl1.start();
26         dl2.start();
27     }
28 }

3、生产者消费者问题代码1

1 package cn.itcast_03;
2 
3 public class Student {
4     String name;
5     int age;
6 }
 1 package cn.itcast_03;
 2 
 3 public class SetThread implements Runnable {
 4 
 5     private Student s;
 6 
 7     public SetThread(Student s) {
 8         this.s = s;
 9     }
10 
11     @Override
12     public void run() {
13         // Student s = new Student();
14         s.name = "林青霞";
15         s.age = 27;
16     }
17 
18 }
 1 package cn.itcast_03;
 2 
 3 public class GetThread implements Runnable {
 4     private Student s;
 5 
 6     public GetThread(Student s) {
 7         this.s = s;
 8     }
 9 
10     @Override
11     public void run() {
12         // Student s = new Student();
13         System.out.println(s.name + "---" + s.age);
14     }
15 
16 }
 1 package cn.itcast_03;
 2 
 3 /*
 4  * 分析:
 5  *         资源类:Student    
 6  *         设置学生数据:SetThread(生产者)
 7  *         获取学生数据:GetThread(消费者)
 8  *         测试类:StudentDemo
 9  * 
10  * 问题1:按照思路写代码,发现数据每次都是:null---0
11  * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个
12  * 如何实现呢?
13  *         在外界把这个数据创建出来,通过构造方法传递给其他的类。
14  * 
15  */
16 public class StudentDemo {
17     public static void main(String[] args) {
18         //创建资源
19         Student s = new Student();
20         
21         //设置和获取的类
22         SetThread st = new SetThread(s);
23         GetThread gt = new GetThread(s);
24 
25         //线程类
26         Thread t1 = new Thread(st);
27         Thread t2 = new Thread(gt);
28 
29         //启动线程
30         t1.start();
31         t2.start();
32     }
33 }

4、生产者消费者题代码2并解决线程安全问题

1 package cn.itcast_04;
2 
3 public class Student {
4     String name;
5     int age;
6 }
Student
 1 package cn.itcast_04;
 2 
 3 public class SetThread implements Runnable {
 4 
 5     private Student s;
 6     private int x = 0;
 7 
 8     public SetThread(Student s) {
 9         this.s = s;
10     }
11 
12     @Override
13     public void run() {
14         while (true) {
15             synchronized (s) {
16                 if (x % 2 == 0) {
17                     s.name = "林青霞";//刚走到这里,就被别人抢到了执行权
18                     s.age = 27;
19                 } else {
20                     s.name = "刘意"; //刚走到这里,就被别人抢到了执行权
21                     s.age = 30;
22                 }
23                 x++;
24             }
25         }
26     }
27 }
 1 package cn.itcast_04;
 2 
 3 public class GetThread implements Runnable {
 4     private Student s;
 5 
 6     public GetThread(Student s) {
 7         this.s = s;
 8     }
 9 
10     @Override
11     public void run() {
12         while (true) {
13             synchronized (s) {
14                 System.out.println(s.name + "---" + s.age);
15             }
16         }
17     }
18 }
 1 package cn.itcast_04;
 2 
 3 /*
 4  * 分析:
 5  *         资源类:Student    
 6  *         设置学生数据:SetThread(生产者)
 7  *         获取学生数据:GetThread(消费者)
 8  *         测试类:StudentDemo
 9  * 
10  * 问题1:按照思路写代码,发现数据每次都是:null---0
11  * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个
12  * 如何实现呢?
13  *         在外界把这个数据创建出来,通过构造方法传递给其他的类。
14  * 
15  * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题
16  *         A:同一个数据出现多次
17  *         B:姓名和年龄不匹配
18  * 原因:
19  *         A:同一个数据出现多次
20  *             CPU的一点点时间片的执行权,就足够你执行很多次。
21  *         B:姓名和年龄不匹配
22  *             线程运行的随机性
23  * 线程安全问题:
24  *         A:是否是多线程环境        是
25  *         B:是否有共享数据        是
26  *         C:是否有多条语句操作共享数据    是
27  * 解决方案:
28  *         加锁。
29  *         注意:
30  *             A:不同种类的线程都要加锁。
31  *             B:不同种类的线程加的锁必须是同一把。
32  */
33 public class StudentDemo {
34     public static void main(String[] args) {
35         //创建资源
36         Student s = new Student();
37         
38         //设置和获取的类
39         SetThread st = new SetThread(s);
40         GetThread gt = new GetThread(s);
41 
42         //线程类
43         Thread t1 = new Thread(st);
44         Thread t2 = new Thread(gt);
45 
46         //启动线程
47         t1.start();
48         t2.start();
49     }
50 }

5、生产者消费者之等待唤醒机制代码实现

1 package cn.itcast_05;
2 
3 public class Student {
4     String name;
5     int age;
6     boolean flag; // 默认情况是没有数据,如果是true,说明有数据
7 }
 1 package cn.itcast_05;
 2 
 3 public class SetThread implements Runnable {
 4 
 5     private Student s;
 6     private int x = 0;
 7 
 8     public SetThread(Student s) {
 9         this.s = s;
10     }
11 
12     @Override
13     public void run() {
14         while (true) {
15             synchronized (s) {
16                 //判断有没有
17                 if(s.flag){
18                     try {
19                         s.wait(); //t1等着,释放锁
20                     } catch (InterruptedException e) {
21                         e.printStackTrace();
22                     }
23                 }
24                 
25                 if (x % 2 == 0) {
26                     s.name = "林青霞";
27                     s.age = 27;
28                 } else {
29                     s.name = "刘意";
30                     s.age = 30;
31                 }
32                 x++; //x=1
33                 
34                 //修改标记
35                 s.flag = true;
36                 //唤醒线程
37                 s.notify(); //唤醒t2,唤醒并不表示你立马可以执行,必须还得抢CPU的执行权。
38             }
39             //t1有,或者t2有
40         }
41     }
42 }
package cn.itcast_05;

public class GetThread implements Runnable {
    private Student s;

    public GetThread(Student s) {
        this.s = s;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (s) {
                if(!s.flag){
                    try {
                        s.wait(); //t2就等待了。立即释放锁。将来醒过来的时候,是从这里醒过来的时候
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                
                System.out.println(s.name + "---" + s.age);
                //林青霞---27
                //刘意---30
                
                //修改标记
                s.flag = false;
                //唤醒线程
                s.notify(); //唤醒t1
            }
        }
    }
}
 1 package cn.itcast_05;
 2 
 3 /*
 4  * 分析:
 5  *         资源类:Student    
 6  *         设置学生数据:SetThread(生产者)
 7  *         获取学生数据:GetThread(消费者)
 8  *         测试类:StudentDemo
 9  * 
10  * 问题1:按照思路写代码,发现数据每次都是:null---0
11  * 原因:我们在每个线程中都创建了新的资源,而我们要求的时候设置和获取线程的资源应该是同一个
12  * 如何实现呢?
13  *         在外界把这个数据创建出来,通过构造方法传递给其他的类。
14  * 
15  * 问题2:为了数据的效果好一些,我加入了循环和判断,给出不同的值,这个时候产生了新的问题
16  *         A:同一个数据出现多次
17  *         B:姓名和年龄不匹配
18  * 原因:
19  *         A:同一个数据出现多次
20  *             CPU的一点点时间片的执行权,就足够你执行很多次。
21  *         B:姓名和年龄不匹配
22  *             线程运行的随机性
23  * 线程安全问题:
24  *         A:是否是多线程环境        是
25  *         B:是否有共享数据        是
26  *         C:是否有多条语句操作共享数据    是
27  * 解决方案:
28  *         加锁。
29  *         注意:
30  *             A:不同种类的线程都要加锁。
31  *             B:不同种类的线程加的锁必须是同一把。
32  * 
33  * 问题3:虽然数据安全了,但是呢,一次一大片不好看,我就想依次的一次一个输出。
34  * 如何实现呢?
35  *         通过Java提供的等待唤醒机制解决。
36  * 
37  * 等待唤醒:
38  *         Object类中提供了三个方法:
39  *             wait():等待
40  *             notify():唤醒单个线程
41  *             notifyAll():唤醒所有线程
42  *         为什么这些方法不定义在Thread类中呢?
43  *             这些方法的调用必须通过锁对象调用,而我们刚才使用的锁对象是任意锁对象。
44  *             所以,这些方法必须定义在Object类中。
45  */
46 public class StudentDemo {
47     public static void main(String[] args) {
48         //创建资源
49         Student s = new Student();
50         
51         //设置和获取的类
52         SetThread st = new SetThread(s);
53         GetThread gt = new GetThread(s);
54 
55         //线程类
56         Thread t1 = new Thread(st);
57         Thread t2 = new Thread(gt);
58 
59         //启动线程
60         t1.start();
61         t2.start();
62     }
63 }

线程的状态转换图及常见执行情况

 6、线程组的概述和使用

 1 package cn.itcast_06;
 2 
 3 public class MyRunnable implements Runnable {
 4 
 5     @Override
 6     public void run() {
 7         for (int x = 0; x < 100; x++) {
 8             System.out.println(Thread.currentThread().getName() + ":" + x);
 9         }
10     }
11 
12 }
 1 package cn.itcast_06;
 2 
 3 /*
 4  * 线程组: 把多个线程组合到一起。
 5  * 它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
 6  */
 7 public class ThreadGroupDemo {
 8     public static void main(String[] args) {
 9         // method1();
10 
11         // 我们如何修改线程所在的组呢?
12         // 创建一个线程组
13         Java多线程学习笔记

自己开发的在线视频下载工具,基于Java多线程

1.1 Java多线程原理

Java学习笔记—多线程(java.util.concurrent.locks包,转载)

java 多线程笔记

2018年4月24日JAVA