首页 > 代码库 > Java笔记(23):多线程(01)

Java笔记(23):多线程(01)

1、多进程及多线程的意义:
多进程:
单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。
举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。
也就是说现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务。
并且呢,可以提高CPU的使用率。
多线程:
多线程的存在,不是提高程序的执行速度。其实是为了提高应用程序的使用率。
程序的执行其实都是在抢CPU的资源,CPU的执行权。
多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多,就会有更高的几率抢到CPU的执行权。
我们是不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。

2、Java程序运行原理和JVM的启动

 1 package cn.itcast_01;
 2 /*
 3  *    进程:
 4  *        正在运行的程序,是系统进行资源分配和调用的独立单位。
 5  *        每一个进程都有它自己的内存空间和系统资源。
 6  *    线程:
 7  *        是进程中的单个顺序控制流,是一条执行路径
 8  *        一个进程如果只有一条执行路径,则称为单线程程序。
 9  *        一个进程如果有多条执行路径,则称为多线程程序。
10  *
11  *  举例:
12  *      扫雷程序,迅雷下载
13  *  
14  *  大家注意两个词汇的区别:并行和并发。
15  *        前者是逻辑上同时发生,指在某一个时间内同时运行多个程序。
16  *        后者是物理上同时发生,指在某一个时间点同时运行多个程序。
17  *
18  * Java程序的运行原理:
19  *         由java命令启动JVM,JVM启动就相当于启动了一个进程。
20  *         接着有该进程创建了一个主线程去调用main方法。
21  * 
22  * 思考题:
23  *         jvm虚拟机的启动是单线程的还是多线程的?
24  *             多线程的。
25  *             原因是垃圾回收线程也要先启动,否则很容易会出现内存溢出。
26  *             现在的垃圾回收线程加上前面的主线程,最少启动了两个线程,所以,jvm的启动其实是多线程的。
27  */
28 public class MyThreadDemo {
29     public static void main(String[] args) {
30         System.out.println("hello");
31         new Object();
32         new Object();
33         new Object();
34         new Object();
35         //...
36         System.out.println("world");
37     }
38 }

3、实现多线程的方式和思路1

 1 package cn.itcast_02;
 2 
 3 /*
 4  * 需求:我们要实现多线程的程序。
 5  * 如何实现呢?
 6  *         由于线程是依赖进程而存在的,所以我们应该先创建一个进程出来。
 7  *         而进程是由系统创建的,所以我们应该去调用系统功能创建一个进程。
 8  *         Java是不能直接调用系统功能的,所以,我们没有办法直接实现多线程程序。
 9  *         但是呢?Java可以去调用C/C++写好的程序来实现多线程程序。
10  *         由C/C++去调用系统功能创建进程,然后由Java去调用这样的东西,
11  *         然后提供一些类供我们使用。我们就可以实现多线程程序了。
12  * 那么Java提供的类是什么呢?
13  *         Thread
14  *         通过查看API,我们知道了有2中方式实现多线程程序。
15  * 
16  * 方式1:继承Thread类。
17  * 步骤
18  *         A:自定义类MyThread继承Thread类。
19  *         B:MyThread类里面重写run()?
20  *             为什么是run()方法呢?
21  *         C:创建对象
22  *         D:启动线程
23  */
24 public class MyThreadDemo {
25     public static void main(String[] args) {
26         // 创建线程对象
27         // MyThread my = new MyThread();
28         // // 启动线程
29         // my.run();
30         // my.run();
31         // 调用run()方法为什么是单线程的呢?
32         // 因为run()方法直接调用其实就相当于普通的方法调用,所以你看到的是单线程的效果
33         // 要想看到多线程的效果,就必须说说另一个方法:start()
34         // 面试题:run()和start()的区别?
35         // run():仅仅是封装被线程执行的代码,直接调用是普通方法
36         // start():首先启动了线程,然后再由jvm去调用该线程的run()方法。
37         // MyThread my = new MyThread();
38         // my.start();
39         // // IllegalThreadStateException:非法的线程状态异常
40         // // 为什么呢?因为这个相当于是my线程被调用了两次。而不是两个线程启动。
41         // my.start();
42 
43         // 创建两个线程对象
44         MyThread my1 = new MyThread();
45         MyThread my2 = new MyThread();
46 
47         my1.start();
48         my2.start();
49     }
50 }
 1 package cn.itcast_02;
 2 
 3 /*
 4  * 该类要重写run()方法,为什么呢?
 5  * 不是类中的所有代码都需要被线程执行的。
 6  * 而这个时候,为了区分哪些代码能够被线程执行,java提供了Thread类中的run()用来包含那些被线程执行的代码。
 7  */
 8 public class MyThread extends Thread {
 9 
10     @Override
11     public void run() {
12         // 自己写代码
13         // System.out.println("好好学习,天天向上");
14         // 一般来说,被线程执行的代码肯定是比较耗时的。所以我们用循环改进
15         for (int x = 0; x < 100; x++) {
16             System.out.println(x);
17         }
18     }
19 
20 }

4、获取和设置线程对象名称

 1 package cn.itcast_03;
 2 
 3 /*
 4  * 如何获取线程对象的名称呢?
 5  * public final String getName():获取线程的名称。
 6  * 如何设置线程对象的名称呢?
 7  * public final void setName(String name):设置线程的名称
 8  * 
 9  * 针对不是Thread类的子类中如何获取线程对象名称呢?
10  * public static Thread currentThread():返回当前正在执行的线程对象
11  * Thread.currentThread().getName()
12  */
13 public class MyThreadDemo {
14     public static void main(String[] args) {
15         // 创建线程对象
16         //无参构造+setXxx()
17         // MyThread my1 = new MyThread();
18         // MyThread my2 = new MyThread();
19         // //调用方法设置名称
20         // my1.setName("林青霞");
21         // my2.setName("刘意");
22         // my1.start();
23         // my2.start();
24         
25         //带参构造方法给线程起名字
26         // MyThread my1 = new MyThread("林青霞");
27         // MyThread my2 = new MyThread("刘意");
28         // my1.start();
29         // my2.start();
30         
31         //我要获取main方法所在的线程对象的名称,该怎么办呢?
32         //遇到这种情况,Thread类提供了一个很好玩的方法:
33         //public static Thread currentThread():返回当前正在执行的线程对象
34         System.out.println(Thread.currentThread().getName());
35     }
36 }
37 
38 /*
39 名称为什么是:Thread-? 编号
40 
41 class Thread {
42     private char name[];
43 
44     public Thread() {
45         init(null, null, "Thread-" + nextThreadNum(), 0);
46     }
47     
48     private void init(ThreadGroup g, Runnable target, String name,
49                       long stackSize) {
50         init(g, target, name, stackSize, null);
51     }
52     
53      private void init(ThreadGroup g, Runnable target, String name,
54                       long stackSize, AccessControlContext acc) {
55         //大部分代码被省略了
56         this.name = name.toCharArray();
57     }
58     
59     public final void setName(String name) {
60         this.name = name.toCharArray();
61     }
62     
63     
64     private static int threadInitNumber; //0,1,2
65     private static synchronized int nextThreadNum() {
66         return threadInitNumber++; //return 0,1
67     }
68     
69     public final String getName() {
70         return String.valueOf(name);
71     }
72 }
73 
74 class MyThread extends Thread {
75     public MyThread() {
76         super();
77     }
78 }
79 
80 */
 1 package cn.itcast_03;
 2 
 3 public class MyThread extends Thread {
 4 
 5     public MyThread() {
 6     }
 7     
 8     public MyThread(String name){
 9         super(name);
10     }
11 
12     @Override
13     public void run() {
14         for (int x = 0; x < 100; x++) {
15             System.out.println(getName() + ":" + x);
16         }
17     }
18 }

5、线程调度及获取和设置线程优先级

 1 package cn.itcast_04;
 2 
 3 public class ThreadPriority extends Thread {
 4     @Override
 5     public void run() {
 6         for (int x = 0; x < 100; x++) {
 7             System.out.println(getName() + ":" + x);
 8         }
 9     }
10 }
 1 package cn.itcast_04;
 2 
 3 /*
 4  * 我们的线程没有设置优先级,肯定有默认优先级。
 5  * 那么,默认优先级是多少呢?
 6  * 如何获取线程对象的优先级?
 7  *         public final int getPriority():返回线程对象的优先级
 8  * 如何设置线程对象的优先级呢?
 9  *         public final void setPriority(int newPriority):更改线程的优先级。 
10  * 
11  * 注意:
12  *         线程默认优先级是5。
13  *         线程优先级的范围是:1-10。
14  *         线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。
15  *         
16  * IllegalArgumentException:非法参数异常。
17  * 抛出的异常表明向方法传递了一个不合法或不正确的参数。 
18  * 
19  */
20 public class ThreadPriorityDemo {
21     public static void main(String[] args) {
22         ThreadPriority tp1 = new ThreadPriority();
23         ThreadPriority tp2 = new ThreadPriority();
24         ThreadPriority tp3 = new ThreadPriority();
25 
26         tp1.setName("东方不败");
27         tp2.setName("岳不群");
28         tp3.setName("林平之");
29 
30         // 获取默认优先级
31         // System.out.println(tp1.getPriority());
32         // System.out.println(tp2.getPriority());
33         // System.out.println(tp3.getPriority());
34 
35         // 设置线程优先级
36         // tp1.setPriority(100000);
37         
38         //设置正确的线程优先级
39         tp1.setPriority(10);
40         tp2.setPriority(1);
41 
42         tp1.start();
43         tp2.start();
44         tp3.start();
45     }
46 }

6、线程控制之休眠线程

 1 package cn.itcast_04;
 2 
 3 import java.util.Date;
 4 
 5 public class ThreadSleep extends Thread {
 6     @Override
 7     public void run() {
 8         for (int x = 0; x < 100; x++) {
 9             System.out.println(getName() + ":" + x + ",日期:" + new Date());
10             // 睡眠
11             // 困了,我稍微休息1秒钟
12             try {
13                 Thread.sleep(1000);
14             } catch (InterruptedException e) {
15                 e.printStackTrace();
16             }
17         }
18     }
19 }
 1 package cn.itcast_04;
 2 
 3 /*
 4  * 线程休眠
 5  *        public static void sleep(long millis)
 6  */
 7 public class ThreadSleepDemo {
 8     public static void main(String[] args) {
 9         ThreadSleep ts1 = new ThreadSleep();
10         ThreadSleep ts2 = new ThreadSleep();
11         ThreadSleep ts3 = new ThreadSleep();
12 
13         ts1.setName("林青霞");
14         ts2.setName("林志玲");
15         ts3.setName("林志颖");
16 
17         ts1.start();
18         ts2.start();
19         ts3.start();
20     }
21 }

7、线程控制之加入线程

 1 package cn.itcast_04;
 2 
 3 public class ThreadJoin extends Thread {
 4     @Override
 5     public void run() {
 6         for (int x = 0; x < 100; x++) {
 7             System.out.println(getName() + ":" + x);
 8         }
 9     }
10 }
 1 package cn.itcast_04;
 2 
 3 /*
 4  * public final void join():等待该线程终止。 
 5  */
 6 public class ThreadJoinDemo {
 7     public static void main(String[] args) {
 8         ThreadJoin tj1 = new ThreadJoin();
 9         ThreadJoin tj2 = new ThreadJoin();
10         ThreadJoin tj3 = new ThreadJoin();
11 
12         tj1.setName("李渊");
13         tj2.setName("李世民");
14         tj3.setName("李元霸");
15 
16         tj1.start();
17         try {
18             tj1.join();
19         } catch (InterruptedException e) {
20             e.printStackTrace();
21         }
22         
23         tj2.start();
24         tj3.start();
25     }
26 }

8、线程控制之礼让线程

 1 package cn.itcast_04;
 2 
 3 public class ThreadYield extends Thread {
 4     @Override
 5     public void run() {
 6         for (int x = 0; x < 100; x++) {
 7             System.out.println(getName() + ":" + x);
 8             Thread.yield();
 9         }
10     }
11 }
 1 package cn.itcast_04;
 2 
 3 /*
 4  * public static void yield():暂停当前正在执行的线程对象,并执行其他线程。 
 5  * 让多个线程的执行更和谐,但是不能靠它保证一人一次。
 6  */
 7 public class ThreadYieldDemo {
 8     public static void main(String[] args) {
 9         ThreadYield ty1 = new ThreadYield();
10         ThreadYield ty2 = new ThreadYield();
11 
12         ty1.setName("LZ");
13         ty2.setName("HY");
14 
15         ty1.start();
16         ty2.start();
17     }
18 }

9、线程控制之守护线程

 1 package cn.itcast_04;
 2 
 3 public class ThreadDaemon extends Thread {
 4     @Override
 5     public void run() {
 6         for (int x = 0; x < 100; x++) {
 7             System.out.println(getName() + ":" + x);
 8         }
 9     }
10 }
 1 package cn.itcast_04;
 2 
 3 /*
 4  * public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程。
 5  * 当正在运行的线程都是守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。 
 6  * 
 7  * 游戏:坦克大战。
 8  */
 9 public class ThreadDaemonDemo {
10     public static void main(String[] args) {
11         ThreadDaemon td1 = new ThreadDaemon();
12         ThreadDaemon td2 = new ThreadDaemon();
13 
14         td1.setName("关羽");
15         td2.setName("张飞");
16 
17         // 设置收获线程
18         td1.setDaemon(true);
19         td2.setDaemon(true);
20 
21         td1.start();
22         td2.start();
23 
24         Thread.currentThread().setName("刘备");
25         for (int x = 0; x < 5; x++) {
26             System.out.println(Thread.currentThread().getName() + ":" + x);
27         }
28     }
29 }

10、线程控制之中断线程

 1 package cn.itcast_04;
 2 
 3 import java.util.Date;
 4 
 5 public class ThreadStop extends Thread {
 6     @Override
 7     public void run() {
 8         System.out.println("开始执行:" + new Date());
 9 
10         // 我要休息10秒钟,亲,不要打扰我哦
11         try {
12             Thread.sleep(10000);
13         } catch (InterruptedException e) {
14             // e.printStackTrace();
15             System.out.println("线程被终止了");
16         }
17 
18         System.out.println("结束执行:" + new Date());
19     }
20 }
 1 package cn.itcast_04;
 2 
 3 /*
 4  * public final void stop():让线程停止,过时了,但是还可以使用。
 5  * public void interrupt():中断线程。 把线程的状态终止,并抛出一个InterruptedException。
 6  */
 7 public class ThreadStopDemo {
 8     public static void main(String[] args) {
 9         ThreadStop ts = new ThreadStop();
10         ts.start();
11 
12         // 你超过三秒不醒过来,我就干死你
13         try {
14             Thread.sleep(3000);
15             // ts.stop();
16             ts.interrupt();
17         } catch (InterruptedException e) {
18             e.printStackTrace();
19         }
20     }
21 }

线程的生命周期图解:

技术分享

11、多线程思路及代码实现方式2

 1 package cn.itcast_05;
 2 
 3 public class MyRunnable implements Runnable {
 4 
 5     @Override
 6     public void run() {
 7         for (int x = 0; x < 100; x++) {
 8             // 由于实现接口的方式就不能直接使用Thread类的方法了,但是可以间接的使用
 9             System.out.println(Thread.currentThread().getName() + ":" + x);
10         }
11     }
12 
13 }
 1 package cn.itcast_05;
 2 
 3 /*
 4  * 方式2:实现Runnable接口
 5  * 步骤:
 6  *         A:自定义类MyRunnable实现Runnable接口
 7  *         B:重写run()方法
 8  *         C:创建MyRunnable类的对象
 9  *         D:创建Thread类的对象,并把C步骤的对象作为构造参数传递
10  */
11 public class MyRunnableDemo {
12     public static void main(String[] args) {
13         // 创建MyRunnable类的对象
14         MyRunnable my = new MyRunnable();
15 
16         // 创建Thread类的对象,并把C步骤的对象作为构造参数传递
17         // Thread(Runnable target)
18         // Thread t1 = new Thread(my);
19         // Thread t2 = new Thread(my);
20         // t1.setName("林青霞");
21         // t2.setName("刘意");
22 
23         // Thread(Runnable target, String name)
24         Thread t1 = new Thread(my, "林青霞");
25         Thread t2 = new Thread(my, "刘意");
26 
27         t1.start();
28         t2.start();
29     }
30 }

多线程两种方式的图解比较及区别:

技术分享

练习:继承Thread类的方式卖电影票案例

 1 package cn.itcast_06;
 2 
 3 public class SellTicket extends Thread {
 4 
 5     // 定义100张票
 6     // private int tickets = 100;
 7     // 为了让多个线程对象共享这100张票,我们其实应该用静态修饰
 8     private static int tickets = 100;
 9 
10     @Override
11     public void run() {
12         // 定义100张票
13         // 每个线程进来都会走这里,这样的话,每个线程对象相当于买的是自己的那100张票,这不合理,所以应该定义到外面
14         // int tickets = 100;
15 
16         // 是为了模拟一直有票
17         while (true) {
18             if (tickets > 0) {
19                 System.out.println(getName() + "正在出售第" + (tickets--) + "张票");
20             }
21         }
22     }
23 }
 1 package cn.itcast_06;
 2 
 3 /*
 4  * 某电影院目前正在上映贺岁大片(红高粱,少林寺传奇藏经阁),共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
 5  * 继承Thread类来实现。
 6  */
 7 public class SellTicketDemo {
 8     public static void main(String[] args) {
 9         // 创建三个线程对象
10         SellTicket st1 = new SellTicket();
11         SellTicket st2 = new SellTicket();
12         SellTicket st3 = new SellTicket();
13 
14         // 给线程对象起名字
15         st1.setName("窗口1");
16         st2.setName("窗口2");
17         st3.setName("窗口3");
18 
19         // 启动线程
20         st1.start();
21         st2.start();
22         st3.start();
23     }
24 }

练习2:实现Runnable接口的方式卖电影票案例

 1 package cn.itcast_07;
 2 
 3 public class SellTicket implements Runnable {
 4     // 定义100张票
 5     private int tickets = 100;
 6 
 7     @Override
 8     public void run() {
 9         while (true) {
10             if (tickets > 0) {
11                 System.out.println(Thread.currentThread().getName() + "正在出售第"
12                         + (tickets--) + "张票");
13             }
14         }
15     }
16 }
 1 package cn.itcast_07;
 2 
 3 /*
 4  * 实现Runnable接口的方式实现
 5  */
 6 public class SellTicketDemo {
 7     public static void main(String[] args) {
 8         // 创建资源对象
 9         SellTicket st = new SellTicket();
10 
11         // 创建三个线程对象
12         Thread t1 = new Thread(st, "窗口1");
13         Thread t2 = new Thread(st, "窗口2");
14         Thread t3 = new Thread(st, "窗口3");
15 
16         // 启动线程
17         t1.start();
18         t2.start();
19         t3.start();
20     }
21 }

卖票出现同票和负数票的原因分析:

 1 package cn.itcast_08;
 2 
 3 public class SellTicket implements Runnable {
 4     // 定义100张票
 5     private int tickets = 100;
 6 
 7 //    @Override
 8 //    public void run() {
 9 //        while (true) {
10 //            // t1,t2,t3三个线程
11 //            // 这一次的tickets = 100;
12 //            if (tickets > 0) {
13 //                // 为了模拟更真实的场景,我们稍作休息
14 //                try {
15 //                    Thread.sleep(100); // t1就稍作休息,t2就稍作休息
16 //                } catch (InterruptedException e) {
17 //                    e.printStackTrace();
18 //                }
19 //
20 //                System.out.println(Thread.currentThread().getName() + "正在出售第"
21 //                        + (tickets--) + "张票");
22 //                // 理想状态:
23 //                // 窗口1正在出售第100张票
24 //                // 窗口2正在出售第99张票
25 //                // 但是呢?
26 //                // CPU的每一次执行必须是一个原子性(最简单基本的)的操作。
27 //                // 先记录以前的值
28 //                // 接着把ticket--
29 //                // 然后输出以前的值(t2来了)
30 //                // ticket的值就变成了99
31 //                // 窗口1正在出售第100张票
32 //                // 窗口2正在出售第100张票
33 //
34 //            }
35 //        }
36 //    }
37     
38     @Override
39     public void run() {
40         while (true) {
41             // t1,t2,t3三个线程
42             // 这一次的tickets = 1;
43             if (tickets > 0) {
44                 // 为了模拟更真实的场景,我们稍作休息
45                 try {
46                     Thread.sleep(100); //t1进来了并休息,t2进来了并休息,t3进来了并休息,
47                 } catch (InterruptedException e) {
48                     e.printStackTrace();
49                 }
50 
51                 System.out.println(Thread.currentThread().getName() + "正在出售第"
52                         + (tickets--) + "张票");
53                 //窗口1正在出售第1张票,tickets=0
54                 //窗口2正在出售第0张票,tickets=-1
55                 //窗口3正在出售第-1张票,tickets=-2
56             }
57         }
58     }
59 }
 1 package cn.itcast_08;
 2 
 3 /*
 4  * 实现Runnable接口的方式实现
 5  * 
 6  * 通过加入延迟后,就产生了连个问题:
 7  * A:相同的票卖了多次
 8  *         CPU的一次操作必须是原子性的
 9  * B:出现了负数票
10  *         随机性和延迟导致的
11  */
12 public class SellTicketDemo {
13     public static void main(String[] args) {
14         // 创建资源对象
15         SellTicket st = new SellTicket();
16 
17         // 创建三个线程对象
18         Thread t1 = new Thread(st, "窗口1");
19         Thread t2 = new Thread(st, "窗口2");
20         Thread t3 = new Thread(st, "窗口3");
21 
22         // 启动线程
23         t1.start();
24         t2.start();
25         t3.start();
26     }
27 }

12、同步代码块的方式解决线程安全问题

 1 package cn.itcast_09;
 2 
 3 public class SellTicket implements Runnable {
 4     // 定义100张票
 5     private int tickets = 100;
 6     //创建锁对象
 7     private Object obj = new Object();
 8 
 9 //    @Override
10 //    public void run() {
11 //        while (true) {
12 //            synchronized(new Object()){
13 //                if (tickets > 0) {
14 //                    try {
15 //                        Thread.sleep(100); 
16 //                    } catch (InterruptedException e) {
17 //                        e.printStackTrace();
18 //                    }
19 //                    System.out.println(Thread.currentThread().getName() + "正在出售第"
20 //                            + (tickets--) + "张票");
21 //                }
22 //            }
23 //        }
24 //    }
25     
26     @Override
27     public void run() {
28         while (true) {
29             synchronized (obj) {
30                 if (tickets > 0) {
31                     try {
32                         Thread.sleep(100);
33                     } catch (InterruptedException e) {
34                         e.printStackTrace();
35                     }
36                     System.out.println(Thread.currentThread().getName()
37                             + "正在出售第" + (tickets--) + "张票");
38                 }
39             }
40         }
41     }
42 }
 1 package cn.itcast_09;
 2 
 3 /*
 4  * 如何解决线程安全问题呢?
 5  * 
 6  * 要想解决问题,就要知道哪些原因会导致出问题:(而且这些原因也是以后我们判断一个程序是否会有线程安全问题的标准)
 7  * A:是否是多线程环境
 8  * B:是否有共享数据
 9  * C:是否有多条语句操作共享数据
10  * 
11  * 我们来回想一下我们的程序有没有上面的问题呢?
12  * A:是否是多线程环境    是
13  * B:是否有共享数据    是
14  * C:是否有多条语句操作共享数据    是
15  * 
16  * 由此可见我们的程序出现问题是正常的,因为它满足出问题的条件。
17  * 接下来才是我们要想想如何解决问题呢?
18  * A和B的问题我们改变不了,我们只能想办法去把C改变一下。
19  * 思想:
20  *         把多条语句操作共享数据的代码给包成一个整体,让某个线程在执行的时候,别人不能来执行。
21  * 问题是我们不知道怎么包啊?其实我也不知道,但是Java给我们提供了:同步机制。
22  * 
23  * 同步代码块:
24  *         synchronized(对象){
25  *             需要同步的代码;
26  *         }
27  * 
28  *         A:对象是什么呢?
29  *             我们可以随便创建一个对象试试。
30  *         B:需要同步的代码是哪些呢?
31  *             把多条语句操作共享数据的代码的部分给包起来
32  * 
33  *         注意:
34  *             同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
35  *             多个线程必须是同一把锁。
36  */
37 public class SellTicketDemo {
38     public static void main(String[] args) {
39         // 创建资源对象
40         SellTicket st = new SellTicket();
41 
42         // 创建三个线程对象
43         Thread t1 = new Thread(st, "窗口1");
44         Thread t2 = new Thread(st, "窗口2");
45         Thread t3 = new Thread(st, "窗口3");
46 
47         // 启动线程
48         t1.start();
49         t2.start();
50         t3.start();
51     }
52 }

13、同步代码块解决线程安全问题的解释及同步的特点及好处和弊端

 1 package cn.itcast_10;
 2 
 3 public class SellTicket implements Runnable {
 4 
 5     // 定义100张票
 6     private int tickets = 100;
 7 
 8     // 定义同一把锁
 9     private Object obj = new Object();
10 
11     @Override
12     public void run() {
13         while (true) {
14             // t1,t2,t3都能走到这里
15             // 假设t1抢到CPU的执行权,t1就要进来
16             // 假设t2抢到CPU的执行权,t2就要进来,发现门是关着的,进不去。所以就等着。
17             // 门(开,关)
18             synchronized (obj) { // 发现这里的代码将来是会被锁上的,所以t1进来后,就锁了。(关)
19                 if (tickets > 0) {
20                     try {
21                         Thread.sleep(100); // t1就睡眠了
22                     } catch (InterruptedException e) {
23                         e.printStackTrace();
24                     }
25                     System.out.println(Thread.currentThread().getName()
26                             + "正在出售第" + (tickets--) + "张票 ");
27                     //窗口1正在出售第100张票
28                 }
29             } //t1就出来可,然后就开门。(开)
30         }
31     }
32 }
 1 package cn.itcast_10;
 2 
 3 /*
 4  * 举例:
 5  *         火车上厕所。
 6  * 
 7  * 同步的特点:
 8  *         前提:
 9  *             多个线程
10  *        解决问题的时候要注意:
11  *            多个线程使用的是同一个锁对象
12  * 同步的好处 
13  *        同步的出现解决了多线程的安全问题。
14  * 同步的弊端
15  *        当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
16  */
17 public class SellTicketDemo {
18     public static void main(String[] args) {
19         // 创建资源对象
20         SellTicket st = new SellTicket();
21 
22         // 创建三个线程对象
23         Thread t1 = new Thread(st, "窗口1");
24         Thread t2 = new Thread(st, "窗口2");
25         Thread t3 = new Thread(st, "窗口3");
26 
27         // 启动线程
28         t1.start();
29         t2.start();
30         t3.start();
31     }
32 }

14、同步代码块的锁及同步方法应用和锁的问题

  1 package cn.itcast_11;
  2 
  3 public class SellTicket implements Runnable {
  4 
  5     // 定义100张票
  6     private static int tickets = 100;
  7 
  8     // 定义同一把锁
  9     private Object obj = new Object();
 10     private Demo d = new Demo();
 11 
 12     private int x = 0;
 13     
 14     //同步代码块用obj做锁
 15 //    @Override
 16 //    public void run() {
 17 //        while (true) {
 18 //            synchronized (obj) {
 19 //                if (tickets > 0) {
 20 //                    try {
 21 //                        Thread.sleep(100);
 22 //                    } catch (InterruptedException e) {
 23 //                        e.printStackTrace();
 24 //                    }
 25 //                    System.out.println(Thread.currentThread().getName()
 26 //                            + "正在出售第" + (tickets--) + "张票 ");
 27 //                }
 28 //            }
 29 //        }
 30 //    }
 31     
 32     //同步代码块用任意对象做锁
 33 //    @Override
 34 //    public void run() {
 35 //        while (true) {
 36 //            synchronized (d) {
 37 //                if (tickets > 0) {
 38 //                    try {
 39 //                        Thread.sleep(100);
 40 //                    } catch (InterruptedException e) {
 41 //                        e.printStackTrace();
 42 //                    }
 43 //                    System.out.println(Thread.currentThread().getName()
 44 //                            + "正在出售第" + (tickets--) + "张票 ");
 45 //                }
 46 //            }
 47 //        }
 48 //    }
 49     
 50     @Override
 51     public void run() {
 52         while (true) {
 53             if(x%2==0){
 54                 synchronized (SellTicket.class) {
 55                     if (tickets > 0) {
 56                         try {
 57                             Thread.sleep(100);
 58                         } catch (InterruptedException e) {
 59                             e.printStackTrace();
 60                         }
 61                         System.out.println(Thread.currentThread().getName()
 62                                 + "正在出售第" + (tickets--) + "张票 ");
 63                     }
 64                 }
 65             }else {
 66 //                synchronized (d) {
 67 //                    if (tickets > 0) {
 68 //                        try {
 69 //                            Thread.sleep(100);
 70 //                        } catch (InterruptedException e) {
 71 //                            e.printStackTrace();
 72 //                        }
 73 //                        System.out.println(Thread.currentThread().getName()
 74 //                                + "正在出售第" + (tickets--) + "张票 ");
 75 //                    }
 76 //                }
 77                 
 78                 sellTicket();
 79                 
 80             }
 81             x++;
 82         }
 83     }
 84 
 85 //    private void sellTicket() {
 86 //        synchronized (d) {
 87 //            if (tickets > 0) {
 88 //            try {
 89 //                    Thread.sleep(100);
 90 //            } catch (InterruptedException e) {
 91 //                    e.printStackTrace();
 92 //            }
 93 //            System.out.println(Thread.currentThread().getName()
 94 //                        + "正在出售第" + (tickets--) + "张票 ");
 95 //            }
 96 //        }
 97 //    }
 98     
 99     //如果一个方法一进去就看到了代码被同步了,那么我就再想能不能把这个同步加在方法上呢?
100 //     private synchronized void sellTicket() {
101 //            if (tickets > 0) {
102 //            try {
103 //                    Thread.sleep(100);
104 //            } catch (InterruptedException e) {
105 //                    e.printStackTrace();
106 //            }
107 //            System.out.println(Thread.currentThread().getName()
108 //                        + "正在出售第" + (tickets--) + "张票 ");
109 //            }
110 //    }
111     
112     private static synchronized void sellTicket() {
113         if (tickets > 0) {
114         try {
115                 Thread.sleep(100);
116         } catch (InterruptedException e) {
117                 e.printStackTrace();
118         }
119         System.out.println(Thread.currentThread().getName()
120                     + "正在出售第" + (tickets--) + "张票 ");
121         }
122 }
123 }
124 
125 class Demo {
126 }
 1 package cn.itcast_11;
 2 
 3 /*
 4  * A:同步代码块的锁对象是谁呢?
 5  *         任意对象。
 6  * 
 7  * B:同步方法的格式及锁对象问题?
 8  *         把同步关键字加在方法上。
 9  * 
10  *         同步方法是谁呢?
11  *             this
12  * 
13  * C:静态方法及锁对象问题?
14  *         静态方法的锁对象是谁呢?
15  *             类的字节码文件对象。(反射会讲)
16  */
17 public class SellTicketDemo {
18     public static void main(String[] args) {
19         // 创建资源对象
20         SellTicket st = new SellTicket();
21 
22         // 创建三个线程对象
23         Thread t1 = new Thread(st, "窗口1");
24         Thread t2 = new Thread(st, "窗口2");
25         Thread t3 = new Thread(st, "窗口3");
26 
27         // 启动线程
28         t1.start();
29         t2.start();
30         t3.start();
31     }
32 }

15、以前的线程安全的类回顾

 1 package cn.itcast_12;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Collections;
 5 import java.util.Hashtable;
 6 import java.util.List;
 7 import java.util.Vector;
 8 
 9 public class ThreadDemo {
10     public static void main(String[] args) {
11         // 线程安全的类
12         StringBuffer sb = new StringBuffer();
13         Vector<String> v = new Vector<String>();
14         Hashtable<String, String> h = new Hashtable<String, String>();
15 
16         // Vector是线程安全的时候才去考虑使用的,但是我还说过即使要安全,我也不用你
17         // 那么到底用谁呢?
18         // public static <T> List<T> synchronizedList(List<T> list)
19         List<String> list1 = new ArrayList<String>();// 线程不安全
20         List<String> list2 = Collections
21                 .synchronizedList(new ArrayList<String>()); // 线程安全
22     }
23 }

--品尝过的失落,消化成温暖的情歌,等你伤心的时候点播... ...

Java笔记(23):多线程(01)