黑马程序员---java基础之多线程
2013-03-14 18:19
567 查看
-------
android培训、java培训、期待与您交流! ----------
多线程的概念
进程:正在执行的程序。
线程:是进程中用于控制程序执行的控制单元(执行路径,执行情景)
一个进程中至少有一个线程。
对于JVM,启动时,只好有两个线程:jvm的主线程。jvm的垃圾回收线程。
线程的状态。
1,被创建。
2,运行。
3,冻结。
4,消亡。(也就是结束)
其实还有一种特殊的状态:临时状态。
临时状态的特点:具备了执行资格,但不具备执行权.
冻结状态的特点:放弃了执行资格。
自定义线程(线程的创建):
Java给我们提供了对象线程这类事物的描述。该类是Thread
该类中定义了,
创建线程对象的方法(构造函数).
提供了要被线程执行的代码存储的位置(run())
还定义了开启线程运行的方法(start()).
同时还有一些其他的方法用于操作线程:
static Thread currentThead():
String getName():
static void sleep(time)throws InterruptedException:
线程中要运行的代码都是后期定义的。
创建线程的第一种方式是:
继承Thread类。原因:要覆盖run方法,定义线程要运行的代码。
步骤:
1,继承Thread类。
2,覆盖run方法。将线程要运行的代码定义其中。
3,创建Thread类的子类对象,其实就是在创建线程,调用start方法。
方式一的简写形式(匿名内部类)
创建线程的第二种方式:
实现Runnable接口。
步骤:
1,定义了实现Runnable接口。
2,覆盖接口的run方法。将多线程要运行的代码存入其中。
3,创建Thread类的对象(创建线程),并将Runnable接口的子类对象作为参数传递给Thread的构造函数。
为什么要传递?因为线程要运行的代码都在Runnable子类的run方法中存储。所以要将该run方法所属的对象传递给Thread。让Thread线程去使用该对象调用其run方法。
4,调用Thread对象的start方法。开启线程。
方式二的简写形式(匿名内部类)
两种方式的特点:
实现方式,因为避免了单继承的局限性,所以创建线程建议使用第二种方式。
综合示例:卖票小程序,
以上小程序多次运行后会出现0号票乃至-1号票,容易出现安全问题
线程间同步
因为多线程具备随机性。因为是由cpu不断的快速切换造成的。
就有可能会产生多线程的安全问题。
问题的产生的原因:
1,多线程代码中有操作共享数据。
2,多条语句操作该共享数据。
有一个线程对多条操作共享数据的代码执行的一部分。还没有执行完,另一个线程开始参与执行。就会发生数据错误。
解决方法:
当一个线程在执行多条操作共享数据代码时,其他线程即使获取了执行权,也不可以参与操作。
Java就对这种解决方式提供了专业的代码。
Synchronized:
同步的原理:就是将部分操作功能数据的代码进行加锁。
例如:将前面的买票程序加同步
同步的表现形式:
1,同步代码块。
2,同步函数。
两者有什么不同:
同步代码块使用的锁是任意对象。
同步函数使用的锁是this。
注意:对于static的同步函数,使用的锁不是this。是 类名.class 是该类的字节码文件对象。
单例设计模式的懒汉式也要用到同步,不然会有安全问题。
同步的好处:解决了线程的安全问题。
同步的弊端:
较为消耗资源。
同步嵌套后,容易死锁。
同步使用的前提:
1,必须是两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
这是才可以称为这些线程被同步了。
特别注意:同步中要避免嵌套同步,容易死锁
例如:
由于A锁中嵌套了B锁,B锁中嵌套了A锁,很容易出现两个线程拿着各自的锁要对方的锁,造成死锁
JDK1.5 中提供了多线程升级解决方案。
将同步Synchronized替换成现实Lock操作。
Lock:替代了Synchronized
lock 上锁
Unlock 释放锁
newCondition()
释放锁的动作一定要执行.
线程间的通信:
等待/唤醒机制。
也就是常见的生产者消费者问题。
1.当多个生产者消费者出现时,
需要让获取执行权的线程判断标记。
通过while完成。
2.需要将对方的线程唤醒。
仅仅用notify,是不可以的。因为有可能出现只唤醒本方。
有可能会导致,所有线程都等待。
所以可以通过notifyAll的形式来完成 。
wait:进入等待状态
notify();唤醒线程池中等待的第一个线程
notifyAll();唤醒线程池中所有等待线程
都使用在同步中,因为要对持有监视器(锁)的线程操作。
所以要使用在同步中,因为只有同步才具有锁。
对于多个生产者和消费者。
为什么要定义while判断标记。
原因:让被唤醒的线程再一次判断标记。
为什么定义notifyAll,
因为需要唤醒对方线程。
因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待
JDK1.5版本提供了一些新的对象,优化了等待唤醒机制。
1,将synchronized 替换成了Lock接口。
将隐式锁,升级成了显示锁。
Lock
获取锁:lock();
释放锁:unlock();注意:释放的动作一定要执行,所以通常定义在finally中。
获取Condition对象:newCondition();
2,将Object中的wait,notify,notifyAll方法都替换成了Condition的await,signal,signalAll。
和以前不同是:一个同步代码块具备一个锁,该所以具备自己的独立wait和notify方法。
现在是将wait,notify等方法,封装进一个特有的对象Condition,而一个Lock锁上可以有多个Condition对象。
例如:生产者消费者
停止线程:
stop过时。
原理:run方法结束。run方法中通常定义循环,指定控制住循环线程即可结束。
1,定义结束标记。
2,当线程处于了冻结状态,没有执行标记,程序一样无法结束。
这时可以循环,正常退出冻结状态,或者强制结束冻结状态。
强制结束冻结状态:interrupt();目的是线程强制从冻结状态恢复到运行状态。
但是会发生InterruptedException异常。
线程中一些常见方法:
yield():临时暂停,可以让线程是释放执行权。
setDaemon(boolean):将线程标记为后台线程,后台线程和前台线程一样,开启,一样抢执行权运行,
只有在结束时,有区别,当前台线程都运行结束后,后台线程会自动结束。
join():什么意思?等待该线程结束。当A线程执行到了B的.join方法时,A就会处于冻结状态。
A什么时候运行呢?当B运行结束后,A就会具备运行资格,继续运行。
加入线程,可以完成对某个线程的临时加入执行。
wait和sleep的区别:
wait:释放cpu执行权,释放同步中锁。
sleep:释放cpu执行权,不释放同步中锁。
------- android培训、java培训、期待与您交流!
----------
android培训、java培训、期待与您交流! ----------
多线程的概念
进程:正在执行的程序。
线程:是进程中用于控制程序执行的控制单元(执行路径,执行情景)
一个进程中至少有一个线程。
对于JVM,启动时,只好有两个线程:jvm的主线程。jvm的垃圾回收线程。
线程的状态。
1,被创建。
2,运行。
3,冻结。
4,消亡。(也就是结束)
其实还有一种特殊的状态:临时状态。
临时状态的特点:具备了执行资格,但不具备执行权.
冻结状态的特点:放弃了执行资格。
自定义线程(线程的创建):
Java给我们提供了对象线程这类事物的描述。该类是Thread
该类中定义了,
创建线程对象的方法(构造函数).
提供了要被线程执行的代码存储的位置(run())
还定义了开启线程运行的方法(start()).
同时还有一些其他的方法用于操作线程:
static Thread currentThead():
String getName():
static void sleep(time)throws InterruptedException:
线程中要运行的代码都是后期定义的。
创建线程的第一种方式是:
继承Thread类。原因:要覆盖run方法,定义线程要运行的代码。
步骤:
1,继承Thread类。
2,覆盖run方法。将线程要运行的代码定义其中。
3,创建Thread类的子类对象,其实就是在创建线程,调用start方法。
public class Main { public static void main(String[] args) { MyThread thread = new MyThread();//创建自定义线程对象 thread.start();//调用run方法 } } class MyThread extends Thread{//继承Thread类 @Override public void run() {//复写run方法 for(int i=0;i<10;i++){ System.out.println("i = "+i); } } }
方式一的简写形式(匿名内部类)
new Thread(){ @Override public void run() { for(int i=0;i<10;i++){ System.out.println("i = "+i); } } }.start();
创建线程的第二种方式:
实现Runnable接口。
步骤:
1,定义了实现Runnable接口。
2,覆盖接口的run方法。将多线程要运行的代码存入其中。
3,创建Thread类的对象(创建线程),并将Runnable接口的子类对象作为参数传递给Thread的构造函数。
为什么要传递?因为线程要运行的代码都在Runnable子类的run方法中存储。所以要将该run方法所属的对象传递给Thread。让Thread线程去使用该对象调用其run方法。
4,调用Thread对象的start方法。开启线程。
class MyThread implements Runnable{ @Override public void run() { for(int i=0;i<10;i++){ System.out.println("i = "+i); } } } public class Main { public static void main(String[] args) { MyThread myThread = new MyThread(); Thread thread = new Thread(myThread); thread.start(); } }
方式二的简写形式(匿名内部类)
new Thread(new Runnable(){ @Override public void run() { for(int i=0;i<10;i++){ System.out.println("i = "+i); } } }).start();
两种方式的特点:
实现方式,因为避免了单继承的局限性,所以创建线程建议使用第二种方式。
综合示例:卖票小程序,
class Ticket implements Runnable{ private int tick = 100; public void run(){ while(tick>0){//只要还有票就继续卖 System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); } } } } class Main{ 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(); } }
以上小程序多次运行后会出现0号票乃至-1号票,容易出现安全问题
线程间同步
因为多线程具备随机性。因为是由cpu不断的快速切换造成的。
就有可能会产生多线程的安全问题。
问题的产生的原因:
1,多线程代码中有操作共享数据。
2,多条语句操作该共享数据。
有一个线程对多条操作共享数据的代码执行的一部分。还没有执行完,另一个线程开始参与执行。就会发生数据错误。
解决方法:
当一个线程在执行多条操作共享数据代码时,其他线程即使获取了执行权,也不可以参与操作。
Java就对这种解决方式提供了专业的代码。
Synchronized:
同步的原理:就是将部分操作功能数据的代码进行加锁。
例如:将前面的买票程序加同步
class Ticket implements Runnable{ private int tick = 100; public synchronized void run(){//同步函数的形式 while(tick>0){//只要还有票就继续卖 //synchronized(this){//同步代码块的形式 If(tick>0){ System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); } //} } } } } class Main{ 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,同步函数。
两者有什么不同:
同步代码块使用的锁是任意对象。
同步函数使用的锁是this。
注意:对于static的同步函数,使用的锁不是this。是 类名.class 是该类的字节码文件对象。
单例设计模式的懒汉式也要用到同步,不然会有安全问题。
同步的好处:解决了线程的安全问题。
同步的弊端:
较为消耗资源。
同步嵌套后,容易死锁。
同步使用的前提:
1,必须是两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
这是才可以称为这些线程被同步了。
特别注意:同步中要避免嵌套同步,容易死锁
例如:
class DeadLock{ public static void main(String[] args) { Dead d = new Dead(); Thread t1 = new Thread(d); Thread t2 = new Thread(d); t1.start(); t2.start(); } } class Dead implements Runnable{ boolean flag = true; public void run(){ if (flag){ flag = false; while(true){ synchronized(MyLock.locka)//这里用的是A对象锁 { System.out.println(Thread.currentThread().getName()+":if locka"); synchronized(MyLock.lockb)这里用的是B对象锁 { System.out.println(Thread.currentThread().getName()+":if lockb"); } } } } else { while (true){ synchronized(MyLock.lockb)//这里用的是B对象锁 { System.out.println(Thread.currentThread().getName()+":else lockb"); synchronized(MyLock.locka)这里用的是A对象锁 { System.out.println(Thread.currentThread().getName()+":else locka"); } } } } } } class MyLock//自定义锁对象 { static MyLock locka = new MyLock();//锁A static MyLock lockb = new MyLock();//锁B }
由于A锁中嵌套了B锁,B锁中嵌套了A锁,很容易出现两个线程拿着各自的锁要对方的锁,造成死锁
JDK1.5 中提供了多线程升级解决方案。
将同步Synchronized替换成现实Lock操作。
Lock:替代了Synchronized
lock 上锁
Unlock 释放锁
newCondition()
释放锁的动作一定要执行.
线程间的通信:
等待/唤醒机制。
也就是常见的生产者消费者问题。
1.当多个生产者消费者出现时,
需要让获取执行权的线程判断标记。
通过while完成。
2.需要将对方的线程唤醒。
仅仅用notify,是不可以的。因为有可能出现只唤醒本方。
有可能会导致,所有线程都等待。
所以可以通过notifyAll的形式来完成 。
wait:进入等待状态
notify();唤醒线程池中等待的第一个线程
notifyAll();唤醒线程池中所有等待线程
都使用在同步中,因为要对持有监视器(锁)的线程操作。
所以要使用在同步中,因为只有同步才具有锁。
class Res{//定义一个缓冲区,同时也有锁对象的作用 String name; String sex; boolean flag = false; } class Input implements Runnable{ private Res r ; Input(Res r){ this.r = r; } public void run(){ int x = 0; while(true){ synchronized(r){ if(r.flag)//如果缓冲区有数据,就等待 try{r.wait();}catch(Exception e){} if(x==0){ r.name="mike"; r.sex="man"; } else{ r.name="张三"; r.sex = "男男男男男"; } x = (x+1)%2;//写入值转换标记 r.flag = true; r.notify();//唤醒消费线程 } } } } class Output implements Runnable{//消费者线程 private Res r ; Output(Res r){//将传入的缓冲区对象赋值 this.r = r; } public void run(){ while(true){ synchronized(r){ if(!r.flag)/如果缓冲区没数据,就等待,否则就打印,同时改变标记 try{r.wait();}catch(Exception e){} System.out.println(r.name+"...."+r.sex); r.flag = false; r.notify();//唤醒生成线程 } } } } class Main{ public static void main(String[] args) { Res r = new Res();//定义一个统一的锁对象,同时也是缓冲区的作用 Input in = new Input(r);//创建生产者 Output out = new Output(r);//创建消费者 Thread t1 = new Thread(in);////创建生成线程 Thread t2 = new Thread(out);/创建消费线程 t1.start(); t2.start(); } }
对于多个生产者和消费者。
为什么要定义while判断标记。
原因:让被唤醒的线程再一次判断标记。
为什么定义notifyAll,
因为需要唤醒对方线程。
因为只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待
JDK1.5版本提供了一些新的对象,优化了等待唤醒机制。
1,将synchronized 替换成了Lock接口。
将隐式锁,升级成了显示锁。
Lock
获取锁:lock();
释放锁:unlock();注意:释放的动作一定要执行,所以通常定义在finally中。
获取Condition对象:newCondition();
2,将Object中的wait,notify,notifyAll方法都替换成了Condition的await,signal,signalAll。
和以前不同是:一个同步代码块具备一个锁,该所以具备自己的独立wait和notify方法。
现在是将wait,notify等方法,封装进一个特有的对象Condition,而一个Lock锁上可以有多个Condition对象。
例如:生产者消费者
class Main{ public static void main(String[] args){ final Demo d = new Demo(); new Thread(){//消费者线程 public void run() { while(true) d.get(); } }.start(); new Thread(){//生产者线程 @Override public void run() { while(true) d.set(); } }.start(); } } class Demo{ private boolean flag = true;//转换标记 private int num = 0; private Lock lock = new ReentrantLock();//定义一个锁lock Condition con_get = lock.newCondition();//定义一个condition对象con_get Condition con_set = lock.newCondition();//定义一个condition对象con_set public void get(){ lock.lock(); try { while(flag)//如果是空就等待 con_get.await(); System.out.println("消费者"+num+"******"); flag = true;//改变标记 con_set.signal();//唤醒con_set } catch(Exception e){ }finally { lock.unlock(); } } public void set(){ lock.lock(); try { while(!flag)//如果不空就等待 con_set.await(); System.out.println("生产者"+(num++)+"***"); flag = false;//改变标记 con_get.signal();//唤醒con_get } catch(Exception e){ }finally { lock.unlock(); } } }
停止线程:
stop过时。
原理:run方法结束。run方法中通常定义循环,指定控制住循环线程即可结束。
1,定义结束标记。
2,当线程处于了冻结状态,没有执行标记,程序一样无法结束。
这时可以循环,正常退出冻结状态,或者强制结束冻结状态。
强制结束冻结状态:interrupt();目的是线程强制从冻结状态恢复到运行状态。
但是会发生InterruptedException异常。
线程中一些常见方法:
yield():临时暂停,可以让线程是释放执行权。
setDaemon(boolean):将线程标记为后台线程,后台线程和前台线程一样,开启,一样抢执行权运行,
只有在结束时,有区别,当前台线程都运行结束后,后台线程会自动结束。
join():什么意思?等待该线程结束。当A线程执行到了B的.join方法时,A就会处于冻结状态。
A什么时候运行呢?当B运行结束后,A就会具备运行资格,继续运行。
加入线程,可以完成对某个线程的临时加入执行。
wait和sleep的区别:
wait:释放cpu执行权,释放同步中锁。
sleep:释放cpu执行权,不释放同步中锁。
------- android培训、java培训、期待与您交流!
----------
相关文章推荐
- 黑马程序员————java基础————多线程
- 黑马程序员---Java基础学习笔记(多线程-前篇)
- 黑马程序员-Java基础-多线程间通讯
- 黑马程序员----------java基础加强之多线程、单例设计模式
- 黑马程序员:Java基础——多线程的死锁问题
- 黑马程序员--Java基础--06多线程
- 黑马程序员——Java基础——多线程
- 黑马程序员----JAVA基础----函数与数组及多线程2
- 黑马程序员---------Java基础------------多线程简单总结
- 黑马程序员--Java基础学习(多线程)第十一天
- 黑马程序员 Java基础——进程与线程及多线程
- 黑马程序员——Java基础__多线程(下)
- 黑马程序员——Java基础之多线程
- 黑马程序员--Java基础--多线程安全问题
- 黑马程序员——Java基础---多线程
- 黑马程序员---Java基础总结--多线程
- 黑马程序员-JAVA基础-多线程(下)
- 黑马程序员-----java基础十二(java之多线程)
- 黑马程序员:Java基础——多线程之安全问题与同步
- 黑马程序员-java基础之多线程