黑马程序员-------java基础之线程
来源:互联网 发布:java开源用户管理系统 编辑:程序博客网 时间:2024/05/29 16:08
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一 线程是啥?
每一个运行着得程序对应一个进程,是cup分配资源的最小单元。
而每一个进程都包含一个和多个线程,程序(进程)所完成的功能,实际是由一个个独立的线程进行完成的。
比如:
JVM启动的时候会有一个进程:java.exe该进程中至少有一个线程在负责java程序的执行,而且这个
线程运行的代码在于main方法中。该线程成为主线程。
二java中创建线程的方式
方式1:继承实现方式
1 定义一个Thread类的子类
2 复写run() 方法
目的:定义该线程所需要执行的代码。即定义该线程的功能
3 创建该类对象
4 该对象.start()
作用:让创建的线程处于可抢夺CUP的状态(临时状态或就绪状态)
方式2:实现方式
1 定义一个类实现Runnable接口
2 复写run()方法
3 用该类对象作为参数创建一个Thread对象: Thread t = new Thread(实现Runnable接口类的对象)
4 t.start();//让创建的线程处于可争夺CPU的状态
为啥2种方式都要复写run方法?
因为JAVA中定义Thread类来描述线程
1 该类定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
也就是说:thread类中的run方法,用于存储线程要运行的代码。
2 Thread类也实现了Runnable接口
实现方式和继承方式的区别?
实现方式的好处:避免了单继承的局限性,
在定义线程时,建议使用实现方式。
两种方式的区别:
继承Thread类:线程代码存放在Tread子类的run()方法中。
实现Runnabel接口:线程代码在接口子类的run()方法中。
线程的几种状态:
三 线程安全问题
为啥会出现安全问题?
原因:不同线程对线程共享数据进行访问的,多条代码可能只执行一部分之后就停止(CPU的切换)
java 为线程线程安全问题所提供的专业解决办法:
同步代码块:
synchronized(对象)
{
操作共享数据的代码
}
对象相当于锁,持有锁的线程可以再同步代码块中执行,没有锁的线程即使获得了cpu
的执行权,也进不去。保证了对共享数据操作的安全性。
保证多个线程同步的前提:
1 必须是2个或2个以的线程
2 必须保证是同一个锁。
目的: 保证了同步代码块儿中的代码只有一个线程在运行。
同步的缺点:多个线程需要进行锁的判断,较为消耗资源。
如何确定同步代码块儿所加的位置:
1 明确哪些代码是多线程运行的代码。-----Run()中的所有
2 明确共享数据。
3 明确多线程运行代码中的哪些语句是操作共享数据的。//同步代码块的使用地方
-------------------------------------------------------------
同步的第二种实现方式:同步函数
例如:
public synchronized void add(int n)
{
}
同步函数用的是哪一个锁?
函数需要被对象调用,那么函数都有一个所属的引用对象。就使用的锁是this。
所以函数是this
静态函数的同步锁是该方法所在类的字节码文件对象类名.Class;
因为静态函数不存在this引用
死锁:
死锁发生的原因:1 存在多个锁
2 同步中嵌套者同步
示例:
class Test implements Runnable{@Overridepublic void run() {// TODO Auto-generated method stubsynchronized(Lock.locka){ System.out.println("Test locka");synchronized(Lock.lockb){System.out.println("Test lockb");}}}}class Test2 implements Runnable{@Overridepublic void run() {// TODO Auto-generated method stubsynchronized(Lock.lockb){System.out.println("Lockb test2");synchronized(Lock.locka){<pre name="code" class="java"> System.out.println("locka test2");}}}}class Lock{static Object locka = new Object();static Object lockb = new Object();}public class deadTreadDemo {public static void main(String[] args) {// TODO Auto-generated method stubTest t = new Test();Test2 t2 = new Test2();Thread th1 = new Thread(t);Thread th2 = new Thread(t2);th1.start();th2.start();}}当 th1 线程获得cpu后执行到
System.out.println("Test locka");失去了CPU的执行权, 但并未释放同步锁对象locka此时th2 线程获得cpu的执行全当执行到:
System.out.println("Lockb test2");失去了cpu的执行权,但并未释放同步锁对象lockb此时th1再次获得cpu执行权,但并未获得lockb对象因而无法进入下一个同步块,无法执行
System.out.println("Test lockb");而无法结束线程释放同步锁locka当一段时间后Th2线程在获得cpu执行权,但并未获得同步锁对象locka因为无法执行下一个同步块
无法执行
<pre name="code" class="java"> System.out.println("locka test2");无法结束线程释放同步锁lockb
这样就导致2个线程再循环等待资源,死锁。
------------------------------------------------------------------------------------------
线程间的通信:
wait();
notify();
notifyAll();
以上方法都定义在Object类中;因为锁是任意对象,而线程的wait(),notify();notifAll()只是针对某个具体的锁而言的。实质是: 锁对象.wait();锁对象.notify();
通信:一个线程唤醒另一个线程
问题描述:当存在多个线程对某一共享资源进行操作时,线程之间的操作应当满足一定的顺序时(如:先输入才能输出)如何实现?
此时就要用到线程之间的通信机制。
实例:
生产者消费者问题
<pre name="code" class="java">// class Resource { private boolean flag = false;//信号量:标志是否有商品 private String name; private int num =0; public void put(String name)throws InterruptedException//name 和num是共享数据 { synchronized(this)//只是保证同步了 但还并未保证先生产后消费:不能保证生产一个消费一个交替运行 { if(flag)//如果有商品 则生产停止 { wait();//放弃CPU的执行权等待消费者的唤醒 } this.name = name; num++; System.out.println(name+"----生产者-----"+num); flage = true;//有货了 notify();//打电话给消费者说有货了 } } public void get()throws InterruptedException { synchronized(this) { if(!flag)//没有消费品 { wait();//消费者等待 } System.out.println(name+"----消费者-------"+num); flag = false;//没货了 notify();//通知生产者没货了(唤醒生产线程) } } } class Producer implements Runnable{ private Resource r; public Producer(Resource r) { this.r = r; } public void run() { while(true) { try { r.put("商品"); } catch(InterruptedException e) { } } }} class Consumer implements Runnable{ <pre name="code" class="java"><pre name="code" class="java"> private Resource r; public Consumer (Resource r) { this.r = r; } public void run() { while(true) { try { r.get(); } catch(InterruptedException e ) { } } }
}
class ProConDemo1
{
public static void main(String[]arg)
{
Resource r = new Resource();
Producer pro = new Producer (r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
t1.start();
t2.start();
}
}
新问题的产生:
当存在多个消费者线程和多个生产者线程时:
可能会出现:生产多个商品但值被消费一个;或者是生产一个商品被多个消费者消费的情况
产生原因:
1 当线程在if(){}块中wait()之后,当再次获得CPU执行权时不会再对flag进行判断;从而出现多次消费,或者多次生产的现象;
解决办法:将if(flag)改为:while(flag); 让线程始终对flag进行判断
2 改成while()之后出现新的问题:
所有线程都wait()了。让程序无法正常执行。
原因:notify();唤醒的是:先进入等待所队列中的线程,这样有可能唤醒的是己方(同为生产者线程或者同为消费者线程)线程;
解决办法:notifyAll();
具体分析:
<pre name="code" class="java"><img src="http://img.blog.csdn.net/20140822103745437?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMjE5MDQ5OQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" height="710" width="907" alt="" />
JDK1.5中提供新方法解决锁问题:
用实现Lock接口的类 代替synchronized,使用condtion 来进行wait(),notify() 等操作
lock的特点:可以为同一个lock设立多个condition对象,为不同线程分类:如将线程分为生产者线程类,消费者线程类。当线程间进行唤醒操作时可以对同一个lock的不同类线程进行唤醒。
实例:
<pre name="code" class="java"> import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.ReentrantLock;/*JDK 1.5以及之后对锁的处理 * 1 用 Lock接口的实现类对象 替代 synchronized * 2 用 condition 来进行wait(),notify(),notifyAll()操作 * 3 特点: * 1 需显示的加锁 Lock lock.lock(); * 2 显示释放锁 lock.unlock(); * 3 一个锁可对应多个condition对象 * */class Product1//拉煤优化进程间通信 优化代码{ private String name; private int num = 0; private boolean flag = false; private ReentrantLock lock = new ReentrantLock(); private Condition producer_con; private Condition consumer_con ; public Product1(String name) { this.name = name; producer_con = lock.newCondition(); consumer_con = lock.newCondition(); } //生产行为 public void put()throws InterruptedException { lock.lock();//显示调用加锁 try { while(this.flag) { producer_con.await();//可能出现异常,出现异常时必须释放锁 } System.out.println(name+"---"+"生产者----"+(++num));//模拟生产者进行的操作处理 flag = true; //this.notifyAll();//唤醒消费者 consumer_con.signal(); }finally { lock.unlock();//显示释放锁 } } //消费行为 public void get() throws InterruptedException { lock.lock(); try { while(flag == false) { consumer_con.await(); } System.out.println(name+"---"+"消费者--------------"+num); flag = false; //this.notifyAll();//唤醒生产者 producer_con.signal();// 只唤醒 生产者线程不唤醒本方线程 } finally { lock.unlock(); } }}//生产者class Producer1 implements Runnable{ private Product1 product; public Producer1(Product1 product) { this.product = product; } public void run() { while(true) { try{ product.put(); } catch(InterruptedException e) { } } } }//消费者class Consumer1 implements Runnable{ private Product1 product; public Consumer1(Product1 product) { this.product = product; } public void run() { while(true) { try { product.get(); } catch(InterruptedException e) { } } }}public class producerConsumerDmeo2 { public static void main(String[] args) { // TODO Auto-generated method stub Product1 product = new Product1("商品"); Producer1 producer = new Producer1(product); Consumer1 consumer = new Consumer1(product); Thread t1 = new Thread(producer); Thread t2 = new Thread(producer); Thread t3 = new Thread(consumer); Thread t4 = new Thread(consumer); t1.start();//问题的产生:当存在多个生产者,多个消费者时:一个商品被2个消费者消费 t2.start(); t3.start(); t4.start(); }
四 线程中的常用函数
线程停止的方法
stop():已过时
interrupt();
如何停止一个线程:
从本质上说:只有一个方法:结束Run方法。
当开启一个线程时:线程运行的通常是循环结构的代码,所以要结束run();只要控制住循环。
特殊情况:
当线程处于冻结状态。
就不会读取到标记。那么线程就不会结束
当没有指定的方式来让冻结的线程回复运行状态时,这时需要对冻结进行清除。
强制让线程回复到运行状态中来,这样就可以操作标记让线程结束。
interrupt:清除线程的冻结状态
调用一个线程的interrupt()方法时会触发InterruptedException 异常
在异常处理函数中修改循环条件。
setDaemo();
t1.setDaemon();让t1成为守护线程(后台线程);当前台线程(创建t1的线程)结束时t1会自动结束
注意:该方法必须在线程开启之前调用
join()方法
当A线程,执行到了B线程的.join()方法时,A就会等待B线程执行完
A才会执行。
join可以用来临时加入线程。
线程优先级
t.setPriority(int )//优先级 只有10级
1:MIN_PRRORITY
5:NORMAL_PROORITY
10:MAX_PRORITY
yield() 方法;
Thread.yield();//线程会释放执行权。类似sleep();
- <<黑马程序员>>java基础之线程
- 黑马程序员-------java基础之线程
- 黑马程序员---java基础---线程
- 黑马程序员----------------JAVA基础线程
- 黑马程序员——java基础之线程安全
- 黑马程序员————java基础之线程
- 黑马程序员---java基础之多线程
- 黑马程序员-Java基础之多线程总结
- 黑马程序员:Java基础篇之多线程
- 黑马程序员—JAVA基础之多线程
- 黑马程序员java基础线程章节笔记
- 黑马程序员-JAVA基础之多线程
- 黑马程序员——java基础:线程
- 黑马程序员--Java基础之多线程
- 黑马程序员-java基础之多线程
- 黑马程序员--Java基础之多线程
- 黑马程序员 java基础之多线程
- 黑马程序员java基础之多线程
- Getting Started with Solr 4.9 and Django haystack
- ubuntu上搭建工作环境
- 不要用scanf函数!!!!
- 深入理解JavaScript系列(1):编写高质量JavaScript代码的基本要点
- 大家觉得百度改版后效果怎么样?有没有比原来的好
- 黑马程序员-------java基础之线程
- 佛祖保佑,永无BUG
- 杂项设备示例
- Linux常用命令之1
- C# 反射
- 学习Linux 常用命令之2
- C# 泛型
- 学习,Linux常见命令之3
- windows下使用fftw进行傅里叶变换及其编程实例