黑马程序员-Java基础学习第八天总结
2015-09-18 15:12
453 查看
------- android培训、java培训、期待与您交流! ----------
进程:是一个正在执行中的程序
每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
线程:就是进程中的一个独立的控制单元
线程在控制着进程的执行
Java VM 启动时会有一个进程java.exe
该进程中至少一个线程负责Java程序的执行
而且这个线程运行的代码存在于main方法中
该线程称之为主线程
jvm启动不止一个线程,还有负责垃圾回收机制的线程。
创建线程共有两种方式:继承方式和实现方式(简单的说)。
1、 继承方式
通过查找java的帮助文档API,我们发现java中已经提供了对线程这类事物的描述的类——Thread类。这第一种方式就是通过继承Thread类,然后复写其run方法的方式来创建线程。
创建步骤:
a,定义类继承Thread。
b,复写Thread中的run方法。
目的:将自定义代码存储在run方法中,让线程运行。
c,创建定义类的实例对象。相当于创建一个线程。
d,用该对象调用线程的start方法。该方法的作用是:启动线程,调用run方法。
注:如果对象直接调用run方法,等同于只有一个线程在执行,自定义的线程并没有启动。
覆盖run方法的原因:
Thread类用于描述线程。该类就定义了一个功能,用于存储线程要执行的代码。该存储功能就run方法。也就是说,Thread类中的run方法,用于存储线程要运行的代码。
程序示例:
2、 实现方式
使用继承方式有一个弊端,那就是如果该类本来就继承了其他父类,那么就无法通过Thread类来创建线程了。这样就有了第二种创建线程的方式:实现Runnable接口,并复习其中run方法的方式。
创建步骤:
a,定义类实现Runnable的接口。
b,覆盖Runnable接口中的run方法。目的也是为了将线程要运行的代码存放在该run方法中。
c,通过Thread类创建线程对象。
d,将Runnable接口的子
4000
类对象作为实参传递给Thread类的构造方法。
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定对象的run方法,就必须明确该run方法所属对象。
e,调用Thread类中start方法启动线程。start方法会自动调用Runnable接口子类的run方法。
实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
程序示例:
2、几种状态
被创建:等待启动,调用start启动。
运行状态:具有执行资格和执行权。
临时状态(阻塞):有执行资格,但是没有执行权。
冻结状态:遇到sleep(time)方法和wait()方法时,失去执行资格和执行权,sleep方法时间到或者调用notify()方法时,获得执行资格,变为临时状态。
消忙状态:stop()方法,或者run方法结束。
注:当已经从创建状态到了运行状态,再次调用start()方法时,就失去意义了,java运行时会提示线程状态异常。
图解:
四、线程安全问题
1、导致安全问题的出现的原因:
当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没用执行完,另一个线程参与进来执行。导致共享数据的错误。
简单的说就两点:
a、多个线程访问出现延迟。
b、线程随机性 。
注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
2、解决办法——同步
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
在java中对于多线程的安全问题提供了专业的解决方式——synchronized(同步)
这里也有两种解决方式,一种是同步代码块,还有就是同步函数。都是利用关键字synchronized来实现。
a、同步代码块
用法:
synchronized(对象)
{需要被同步的代码}
同步可以解决安全问题的根本原因就在那个对象上。其中对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
示例:
b,同步函数
格式:
在函数上加上synchronized修饰符即可。
那么同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。
3、同步的前提
a,必须要有两个或者两个以上的线程。
b,必须是多个线程使用同一个锁。
4、同步的利弊
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
5、如何寻找多线程中的安全问题
a,明确哪些代码是多线程运行代码。
b,明确共享数据。
c,明确多线程运行代码中哪些语句是操作共享数据的。
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不在是this。因为静态方法中也不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。如:
类名.class 该对象的类型是Class
这就是静态函数所使用的锁。而静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class
六、死锁
当同步中嵌套同步时,就有可能出现死锁现象。
(四)线程间的通信(等待--唤醒机制)
线程间通讯:其实就是多个线程在操作同一个资源,但是操作的动作不同。
操作的方法:
wait(); //线程等待,执行此方法时,释放cpu执行权,并释放锁。
notify(); //唤醒线程池中的一个线程
notifyAll(); //唤醒线程池中所有线程
重点:这些方法都在Object中、都使用在同步中,因为要对持有监视器(锁)的线程操作,等待和唤醒必须是同一个锁。
消费者和生产者代码演示
JDK1.5 中提供了多线程升级解决方案,涉及类Lock,Condition。
将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。该对象可以Lock锁 进行获取。
Lock:替代了Synchronized
lock();
unlock();
newCondition()
Synchronized操作替换操作:
Lock l = ...;
l.lock(); // 上锁
try {
//同步代码块
} finally {
l.unlock(); //解锁
}
Condition:替代了Object 中wait(); notify(); notifyAll();
wait() ——> await();
notify() ——> signal();
notifyAll() ——> signalAll();
(五)线程的停止
api文档中stop()方法已经过时。
停止线程方法:只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法 interrupt();
(六)Thread的其他方法
join():当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。join可以用来临时加入线程执行。
setDaemon():将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。
setPriority(int newPriority):设置线程优先级,级数为1~10
yield() :暂停当前正在执行的线程对象,并执行其他线程。
一、了解进程
进程:是一个正在执行中的程序每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
线程:就是进程中的一个独立的控制单元
线程在控制着进程的执行
一个进程中至少有一个线程
Java VM 启动时会有一个进程java.exe该进程中至少一个线程负责Java程序的执行
而且这个线程运行的代码存在于main方法中
该线程称之为主线程
jvm启动不止一个线程,还有负责垃圾回收机制的线程。
二、创建线程的方式
创建线程共有两种方式:继承方式和实现方式(简单的说)。1、 继承方式
通过查找java的帮助文档API,我们发现java中已经提供了对线程这类事物的描述的类——Thread类。这第一种方式就是通过继承Thread类,然后复写其run方法的方式来创建线程。
创建步骤:
a,定义类继承Thread。
b,复写Thread中的run方法。
目的:将自定义代码存储在run方法中,让线程运行。
c,创建定义类的实例对象。相当于创建一个线程。
d,用该对象调用线程的start方法。该方法的作用是:启动线程,调用run方法。
注:如果对象直接调用run方法,等同于只有一个线程在执行,自定义的线程并没有启动。
覆盖run方法的原因:
Thread类用于描述线程。该类就定义了一个功能,用于存储线程要执行的代码。该存储功能就run方法。也就是说,Thread类中的run方法,用于存储线程要运行的代码。
程序示例:
//线程对象 class Demo extends Thread{ public void run(){ for(int x=0; x<60; x++) System.out.println("demo run----"+x); } } class ThreadDemo { public static void main(String[] args) { Demo d = new Demo();//创建好一个线程。 //d.start();//开启线程并执行该线程的run方法。 d.run();//仅仅是对象调用方法。而线程创建了,并没有运行。 for(int x=0; x<60; x++) System.out.println("Hello World!--"+x); } }
2、 实现方式
使用继承方式有一个弊端,那就是如果该类本来就继承了其他父类,那么就无法通过Thread类来创建线程了。这样就有了第二种创建线程的方式:实现Runnable接口,并复习其中run方法的方式。
创建步骤:
a,定义类实现Runnable的接口。
b,覆盖Runnable接口中的run方法。目的也是为了将线程要运行的代码存放在该run方法中。
c,通过Thread类创建线程对象。
d,将Runnable接口的子
4000
类对象作为实参传递给Thread类的构造方法。
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定对象的run方法,就必须明确该run方法所属对象。
e,调用Thread类中start方法启动线程。start方法会自动调用Runnable接口子类的run方法。
实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
程序示例:
public class RunnableDemo { public static void main(String[] args) { RDemo R = new RDemo(); Thread R1 = new Thread(R);// 创建了一个线程; Thread R2 = new Thread(R);// 创建了一个线程; R1.start(); R2.start(); } } class RDemo implements Runnable{ public void run() { while (true) { System.out.println(Thread.currentThread().getName()); } } }
2、几种状态
被创建:等待启动,调用start启动。
运行状态:具有执行资格和执行权。
临时状态(阻塞):有执行资格,但是没有执行权。
冻结状态:遇到sleep(time)方法和wait()方法时,失去执行资格和执行权,sleep方法时间到或者调用notify()方法时,获得执行资格,变为临时状态。
消忙状态:stop()方法,或者run方法结束。
注:当已经从创建状态到了运行状态,再次调用start()方法时,就失去意义了,java运行时会提示线程状态异常。
图解:
四、线程安全问题
1、导致安全问题的出现的原因:
当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没用执行完,另一个线程参与进来执行。导致共享数据的错误。
简单的说就两点:
a、多个线程访问出现延迟。
b、线程随机性 。
注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
2、解决办法——同步
对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
在java中对于多线程的安全问题提供了专业的解决方式——synchronized(同步)
这里也有两种解决方式,一种是同步代码块,还有就是同步函数。都是利用关键字synchronized来实现。
a、同步代码块
用法:
synchronized(对象)
{需要被同步的代码}
同步可以解决安全问题的根本原因就在那个对象上。其中对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
示例:
//多线程售票案例 class Ticket implements Runnable{ private int tick = 1000; Object obj = new Object(); public void run(){ while(true){ synchronized(obj){ //同步代码块,保证线程安全 if(tick>0){ System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); } } } } } 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(); } }
b,同步函数
格式:
在函数上加上synchronized修饰符即可。
那么同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。
3、同步的前提
a,必须要有两个或者两个以上的线程。
b,必须是多个线程使用同一个锁。
4、同步的利弊
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
5、如何寻找多线程中的安全问题
a,明确哪些代码是多线程运行代码。
b,明确共享数据。
c,明确多线程运行代码中哪些语句是操作共享数据的。
五、静态函数的同步方式
如果同步函数被静态修饰后,使用的锁是什么呢?通过验证,发现不在是this。因为静态方法中也不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。如:
类名.class 该对象的类型是Class
这就是静态函数所使用的锁。而静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class
/* 加同步的单例设计模式————懒汉式 */ class Single { private static Single s = null; private Single(){} public static void getInstance() { if(s==null) { synchronized(Single.class) { if(s==null) s = new Single(); } } return s; } }
六、死锁
当同步中嵌套同步时,就有可能出现死锁现象。
//一个死锁的例子 package cn.thread; public class DeadLockDemo { public static void main(String[] args) { DeadLock dl1 = new DeadLock(); DeadLock dl2 = new DeadLock(); Thread dlt1 = new Thread(dl1); Thread dlt2 = new Thread(dl2); dl1.flag = 1; dl2.flag = 2; dlt1.start(); dlt2.start(); } } class DeadLock implements Runnable { int flag = 1; static String str1 = new String("1"); static String str2 = new String("2"); @Override public void run() { if (flag == 1) { while (true) { synchronized (str1) { System.out.println("AAAA--11111111"); synchronized (str2) { System.out.println("AAAA--22222222"); } } } } else if (flag == 2) { while (true) { synchronized (str2) { System.out.println("BBBB--11111111"); synchronized (str1) { System.out.println("BBBB--22222222"); } } } } } }
(四)线程间的通信(等待--唤醒机制)
线程间通讯:其实就是多个线程在操作同一个资源,但是操作的动作不同。
操作的方法:
wait(); //线程等待,执行此方法时,释放cpu执行权,并释放锁。
notify(); //唤醒线程池中的一个线程
notifyAll(); //唤醒线程池中所有线程
重点:这些方法都在Object中、都使用在同步中,因为要对持有监视器(锁)的线程操作,等待和唤醒必须是同一个锁。
消费者和生产者代码演示
/** * 线程通讯 * 消费者和生产者代码演示 * @author Administrator * */ public class ProducerConsumerDemo3 { public static void main(String[] args) { Resource r=new Resource(); new Thread(new Producer(r)).start(); new Thread(new Producer(r)).start(); new Thread(new Consumer(r)).start(); new Thread(new Consumer(r)).start(); } } /* *生产者 */ class Producer implements Runnable{ private Resource r; Producer(Resource r){ this.r=r; } int bFull=0; //判断是否有生产的商品标记 @Override public void run() { while(true){ r.put(); } } } /* * 消费者 */ class Consumer implements Runnable{ private Resource r; Consumer(Resource r){ this.r=r; } @Override public void run() { while(true){ r.get(); } } } /* * 资源类 */ class Resource{ private int count=0; boolean bFull=false; //生产商品 public synchronized void put() { //如果商品存在,线程等待 while(bFull){ //while判断标记,让被唤醒的线程再一次判断标记。 try { wait();} catch (InterruptedException e) {} } //生产产品,将bFull标记为true,并唤醒所有的线程 System.out.println(Thread.currentThread().getName()+"....生产者....+商品+"+ ++count); bFull=true; notifyAll(); } public synchronized void get() { while(! bFull){ try {wait();} catch (Exception e) {} } //消费产品,将bFull标记为false,并唤醒所有的线程 System.out.println(Thread.currentThread().getName()+"。。。。。消费者。。。。。+商品+"+ count); bFull=false; notifyAll(); } }
JDK1.5 中提供了多线程升级解决方案,涉及类Lock,Condition。
将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。该对象可以Lock锁 进行获取。
Lock:替代了Synchronized
lock();
unlock();
newCondition()
Synchronized操作替换操作:
Lock l = ...;
l.lock(); // 上锁
try {
//同步代码块
} finally {
l.unlock(); //解锁
}
Condition:替代了Object 中wait(); notify(); notifyAll();
wait() ——> await();
notify() ——> signal();
notifyAll() ——> signalAll();
/* 资源类,JDK1.5 中提供了多线程升级解决方案,Lock,Condition。 */ class Resource{ //定义锁对象 final Lock lock=new ReentrantLock(); final Condition condition_pro=lock.newCondition(); final Condition condition_con=lock.newCondition(); private int count=0; boolean bFull=false; //生产商品 public void put() { lock.lock(); try { //如果商品存在,线程等待 while(bFull){ //while判断标记,让被唤醒的线程再一次判断标记。 try { condition_pro.await();} catch (InterruptedException e) {} } //生产产品,将bFull标记为true,并唤醒所有的线程 System.out.println(Thread.currentThread().getName()+"....生产者....+商品+"+ ++count); bFull=true; //唤醒对方的一个线程 condition_con.signal(); }finally{ lock.unlock(); //释放锁 } } public void get() { lock.lock(); try { while(! bFull){ try {condition_con.await();} catch (Exception e) {} } //消费产品,将bFull标记为false,并唤醒所有的线程 System.out.println(Thread.currentThread().getName()+"。。。。。消费者。。。。。+商品+"+ count); bFull=false; //唤醒对方的一个线程 condition_pro.signal(); }finally{ lock.unlock(); //释放锁 } } }
(五)线程的停止
api文档中stop()方法已经过时。
停止线程方法:只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法 interrupt();
class StopThread implements Runnable { private boolean flag = true; public void run() { while (flag) { try { wait(); } catch (Exception e) { System.out.println(Thread.currentThread().getName() + "....run"); flag=false; } } } // 标志Flag为false public void changeFlag() { flag = false; } } class StopThreadDemo { public static void main(String[] args) { StopThread st = new StopThread(); Thread t1 = new Thread(st); Thread t2 = new Thread(st); t1.setDaemon(true); t2.setDaemon(true); t1.start(); t2.start(); int num = 0; while (true) { if (num++ == 60) { // st.changeFlag(); // t1.interrupt(); // t2.interrupt(); break; } System.out.println(Thread.currentThread().getName() + "......."+ num); } System.out.println("over"); } }
(六)Thread的其他方法
join():当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。join可以用来临时加入线程执行。
setDaemon():将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。
setPriority(int newPriority):设置线程优先级,级数为1~10
yield() :暂停当前正在执行的线程对象,并执行其他线程。
相关文章推荐
- Java实现栈和队列面试题
- 程序员该有的艺术气质—SOLID原则
- 黑马程序员-Java基础学习第七天总结
- 面试题 33
- “黑马程序员”HashSet
- 2015最火十大Android开源项目,是个程序员你就该看看!
- 什么情况让程序员处于水深火热中-兄弟连IT教育
- 程序员的自我修炼
- 程序员的噩梦,我的漏洞伤害了他人
- 2015程序员值得收藏的十大主流button模式
- 2015最火十大Android开源项目,是个程序员你就该看看!
- 面试题 31
- 一道面试题Lintcode196-Find the Missing Number
- 面试题总结-new 和malloc 区别
- 谈女程序员的问题
- 谈一谈我所理解的面试
- 30多年程序员生涯经验总结
- 黑马程序员-Java基础学习第六天总结
- 黑马程序员-Java基础学习第五天总结
- 12款令程序员惊叹的CSS3效果库