黑马程序员--JAVA中的多线程
来源:互联网 发布:mysql unique查询 编辑:程序博客网 时间:2024/05/29 00:30
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、创建多线程
1、多线程的两种创建方式
java.lang.Thread类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行,由于线程类本身就是调用的Runnable接口所以可以继承java.lang.Thread 类或者直接实现Runnable接口来重写run()方法实现线程。
2、继承Thread类的多线程创建方式
流程为
1>定义一个类继承Thread类。
2> 覆盖Thread类中的run方法。
3>直接创建Thread的子类对象创建线程。
4>调用start方法开启线程并调用线程的任务run方法执行。
示例:
class Demo extends Thread{ private String name ; Demo(String name){ this.name = name; } public void run(){ for(int x = 0; x < 10; x++){ System.out.println(name + "...x=" + x + "...ThreadName=" + Thread.currentThread ().getName()); } }}class ThreadDemo{ public static void main(String[] args){ Demo d1 = new Demo("第一个"); Demo d2 = new Demo("2222"); d1.start(); //开启线程0,调用run方法。 d2.start();<pre name="code" class="java"> //开启线程1,调用run方法。for(int x = 0; x < 20; x++){ System.out.println("x = " + x + "...over..." + Thread.currentThread().getName()); } }}
3、实现Runnable接口的多线程创建方式
流程为
1> 定义类实现Runnable接口。
2>覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3>通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。为什么?因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务。
4>调用线程对象的start方法开启线程。
示例
//准备扩展Demo类的功能,让其中的内容可以作为线程的任务执行。//通过接口的形式完成。class Demo implements Runnable{//重写run方法,标志需要多线程实现的内容 public void run(){ show(); } public void show(){ for(int x = 0; x < 20; x++){ System.out.println(Thread.currentThread().getName() + "..." + x); } }}class ThreadDemo{ public static void main(String[] args){ Demo d = new Demo(); Thread t1 = new Thread(d);//创建带有开启线程方法Thread的对象 Thread t2 = new Thread(d); t1.start();//开启线程0 t2.start();//开启线程1 }}
4、两种创建方式的比较
将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象避免了Java单继承的局限性。所以,创建线程实现Runnable接口的方式较为常用。
二、多线程的安全问题
1、 线程安全问题产生的原因:
1> 多个线程在操作共享的数据。
2>操作共享数据的线程代码有多条。
3>当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算, 就会导致线程安全问题的产生。
2、线程安全问题的解决方案:
1>同步代码块:
格式: synchronized(对象){
需要被同步的代码;
}
2>同步函数:
格式:在函数上加上synchronized修饰符即可。
示例: 需求:模拟火车站4个窗口同时卖100张票。
/* * 思路: * 有多个线程在操作共享的数据。 * 当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算,就会导致线 程安全问题的产生 * 需要同步代码块解决 */class Ticket implements Runnable{ private int num = 100; Object obj = new Object(); public void run(){ while(true ){ //添加同步代码块,同时只能有一个线程执行代码块内容 synchronized(obj ){ if(num > 0){ System.out.println(Thread.currentThread().getName() + "...sale..." + num--); } } } }}class TicketDemo{ public static void main(String[] args){ Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); Thread t4 = new Thread(t); //开启多线程 t1.start(); t2.start(); t3.start(); t4.start(); }}
三、线程间的通讯
1、为什么需要线程间的通讯?
多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。
2、如何解决线程间的通讯问题?
Java提供了3个非常重要的方法来巧妙地解决线程间的通信问题。这3个方法分别是:wait()、notify()和notifyAll()。它们都是Object类的最终方法,因此每一个类都默认拥有它们。虽然所有的类都默认拥有这3个方法,但是只有在synchronized关键字作用的范围内,并且是同一个同步问题中搭配使用这3个方法时才有实际的意义。
3、多线程间通讯需要注意的问题:
1>这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。
2>必须要明确到底操作的是哪个锁上的线程!
4、具体步骤:
下面这个程序演示了多个线程之间进行通信的具体实现过程。程序中用到了4个类,其中ShareData类用来定义共享数据和同步方法。在同步方法中调用了wait()方法和notify()方法,并通过一个信号量来实现线程间的消息传递。
示例:生产者和消费者之间的消息传递过程
class ShareData { private char c; private boolean isProduced = false; // 信号量 public synchronized void putShareChar(char c) // 具备同步的方法putShareChar() { if (isProduced) // 如果产品还未消费,则生产者等待 { try { wait(); // 生产者等待 }catch(InterruptedException e) { e.printStackTrace(); } } this.c = c; isProduced = true; // 标记已经生产 notify(); // 通知消费者已经生产,可以消费 } public synchronized char getShareChar() // 同步方法getShareChar() { if (!isProduced) // 如果产品还未生产,则消费者等待 { try { wait(); // 消费者等待 }catch(InterruptedException e) { e.printStackTrace(); } } isProduced = false; // 标记已经消费 notify(); // 通知需要生产 return this.c; } } class Producer extends Thread // 生产者线程 { private ShareData s; Producer(ShareData s) { this.s = s; } public void run() { for (char ch = 'A'; ch <= 'D'; ch++) { try { Thread.sleep((int)(Math.random()*3000)); } catch(InterruptedException e) { e.printStackTrace(); } s.putShareChar(ch); // 将产品放入仓库 System.out.println(ch + " is produced by Producer."); } } } class Consumer extends Thread // 消费者线程 { private ShareData s; Consumer(ShareData s) { this.s = s; } public void run() { char ch; do { try { Thread.sleep((int)(Math.random()*3000)); } catch(InterruptedException e) { e.printStackTrace(); } ch = s.getShareChar(); // 从仓库中取出产品 System.out.println(ch + " is consumed by Consumer. "); } while (ch != 'D'); } } class CommunicationDemo { public static void main(String[] args) { ShareData s = new ShareData(); new Consumer(s).start(); new Producer(s).start(); } }
- 黑马程序员 java中的多线程
- 黑马程序员 java中的多线程
- 【黑马程序员】 java中的多线程。
- 黑马程序员-----java中的多线程
- 黑马程序员-Java中的多线程
- 黑马程序员--JAVA中的多线程
- 黑马程序员——java中的多线程
- 黑马程序员——java中的多线程
- 黑马程序员——Java中的多线程
- 黑马程序员----java中的多线程基础
- 黑马程序员-java多线程
- 黑马程序员--java 多线程
- 黑马程序员-java多线程
- 黑马程序员--Java多线程
- 黑马程序员--java多线程
- 黑马程序员:Java多线程
- 黑马程序员 Java多线程
- 黑马程序员---Java多线程
- iOS 拨打电话的几种类型
- 页面渲染
- 为什么HDFS一个块的大小是64MB
- C++primer习题答案中关于迭代器使用的一个错误
- HIVE 调优方法大全
- 黑马程序员--JAVA中的多线程
- 给solr加spell check
- 用HttpClient下载img
- Android&iOS崩溃堆栈上报
- RPM 打包技术与典型 SPEC 文件分析
- Android Studio在Ubuntu14.04下的安装与测试
- Codevs 无限网络发射器选址
- 30多条mysql数据库优化方法,千万级数据库记录查询轻松解决
- navTabPageBreak注意事项以及调试步骤