黑马程序员——学习笔记——多线程
2014-12-12 19:50
483 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流!
-------
时间:2014年12月12日
天气:多云转晴
心情:不好,因为今天花了500大洋,接下来没钱吃饭了T_T
好吧,总算复习了前面两章的东西了,昨天开始看多线程,两天时间,掌握的还行,那就把今天的笔记给弄上来
线程:进程中一个负责程序执行的控制单元(执行路径),一个进程中可以有多个执行路径(多线程)。
注意:一个进程中至少要有一个线程,开启多个线程是为了同时运行多部分代码,每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。
多线程的好处:解决了多部分代码同时运行的问题。
弊端:线程太多,会导致效率的降低。
其实,多个应用程序同时执行都是CPU在做着快速的切换完成的。这个切换是随机的。CPU的切换是需要花费时间的,从而导致了效率的降低。
创建线程的目的就是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行,而运行的指定代码就是这个执行路径的任务。
jvm创建的主线程的任务都定义在了主函数中。
2、覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3、通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
4、调用线程对象的start方法开启线程。
实现Runnable接口的好处:
1、将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。
2,避免了Java单继承的局限性。所以,创建线程的第二种方式较为常用。
同步函数和同步代码块的区别:1、同步函数的锁是固定的this。
2、同步代码块的锁是任意的对象。
建议使用同步代码块
静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class表示
多线程下的单例问题
饿汉式:
饿汉式不存在安全问题,因为不存在多个线程共同操作数据的情况。
懒汉式:
懒汉式存在安全问题,可以使用同步函数解决。
但若直接使用同步函数,则效率较低,因为每次都需要判断。
但若采取如下方式,即可提升效率。
原因在于任何一个线程在执行到第一个if判断语句时,如果Single对象已经创建,则直接获取即可,而不用判断是否能够获取锁,相对于上面使用同步函数的方法就提升了效率。
如果当前线程发现Single对象尚未创建,则再判断是否能够获取锁。
死锁
死锁常见情景之一:同步的嵌套。
例2
线程间通信
线程间通信涉及的方法
多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。
等待/唤醒机制涉及的方法:
① wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
② notify():唤醒线程池中的一个线程(任何一个都有可能)。
③ notifyAll():唤醒线程池中的所有线程。
注意:
1、这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。
2、必须要明确到底操作的是哪个锁上的线程!
3、wait和sleep区别?
①wait可以指定时间也可以不指定。sleep必须指定时间。
②在同步中时,对CPU的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
生产者-消费者问题:
多生产者多消费者问题:
由于if判断标记,只有一次,会导致不该运行的线程运行了,出现了数据错误的情况。故修改成while判断标记,线程获取CPU执行权及锁后,将重新判断是否具备运行条件。
notify方法只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。notifyAll解决了本方线程一定会唤醒对方线程的问题。
JDK1.5新特性-Lock、Condition
同步代码块就是对于锁的操作是隐式的。
JDK1.5以后将同步和锁封装成了对象,并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。
Lock接口:出现替代了同步代码块或者同步函数,将同步的隐式操作变成显示锁操作。同时更为灵活,可以一个锁上加上多组监视器。
lock():获取锁。
unlock():释放锁,为了防止异常出现,导致锁无法被关闭,所以锁的关闭动作要放在finally中。
Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。
Condition接口中的await方法对应于Object中的wait方法。
Condition接口中的signal方法对应于Object中的notify方法。
Condition接口中的signalAll方法对应于Object中的notifyAll方法。
使用一个Lock、两个Condition修改上面的多生产者-多消费者问题:
停止线程
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常就用定义标记来完成。
也可以使用stop方法停止线程,不过已经过时,不再使用。
但是如果线程处于了冻结状态,无法读取标记,如何结束呢?
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格。强制动作会发生InterruptedException,一定要记得处理。
-------
时间:2014年12月12日
天气:多云转晴
心情:不好,因为今天花了500大洋,接下来没钱吃饭了T_T
好吧,总算复习了前面两章的东西了,昨天开始看多线程,两天时间,掌握的还行,那就把今天的笔记给弄上来
多线程
进程:正在进行中的程序(直译),程序运行时,内存开辟的一片空间。线程:进程中一个负责程序执行的控制单元(执行路径),一个进程中可以有多个执行路径(多线程)。
注意:一个进程中至少要有一个线程,开启多个线程是为了同时运行多部分代码,每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。
多线程的好处:解决了多部分代码同时运行的问题。
弊端:线程太多,会导致效率的降低。
其实,多个应用程序同时执行都是CPU在做着快速的切换完成的。这个切换是随机的。CPU的切换是需要花费时间的,从而导致了效率的降低。
创建线程的目的就是为了开启一条执行路径,去运行指定的代码和其他代码实现同时运行,而运行的指定代码就是这个执行路径的任务。
jvm创建的主线程的任务都定义在了主函数中。
创建线程方式一:继承Thread类
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 ThreadDemo2{ public static void main(String[] args){ Demo d1 = new Demo("旺财" ); Demo d2 = new Demo("xiaoqiang" ); d1.start(); //开启线程,调用run方法。 d2.start(); for(int x = 0; x < 20; x++){ System. out.println("x = " + x + "...over..." + Thread.currentThread().getName()); } } }
创建线程方式二:实现Runnable接口
1、定义类实现Runnable接口。2、覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3、通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
4、调用线程对象的start方法开启线程。
实现Runnable接口的好处:
1、将线程的任务从线程的子类中分离出来,进行了单独的封装,按照面向对象的思想将任务封装成对象。
2,避免了Java单继承的局限性。所以,创建线程的第二种方式较为常用。
class Demo implements Runnable{ public void run(){ show(); } public void show(){ for(int x = 0; x < 20; x++){ System. out.println(Thread.currentThread().getName() + "..." + x); } } } class ThreadDemo3{ public static void main(String[] args){ Demo d = new Demo(); Thread t1 = new Thread(d); Thread t2 = new Thread(d); t1.start(); t2.start(); } }
private int sum ; public void add(int num){ synchronized(this ){ sum = sum + num; System. out.println("sum = " + sum); } } } class Cus implements Runnable{ private Bank b = new Bank(); public void run(){ for(int x = 0; x < 3; x++){ b.add(100); } } }
同步函数和同步代码块的区别:1、同步函数的锁是固定的this。
2、同步代码块的锁是任意的对象。
建议使用同步代码块
class Ticket implements Runnable{ private int num = 100; boolean flag = true; public void run(){ if(flag ){ while(true ){ synchronized(this ){ if(num > 0){ try{ Thread. sleep(10); } catch(InterruptedException e){ e.printStackTrace(); } System. out.println(Thread.currentThread().getName() + "...obj..." + num--); } } } } else while(true ) show(); } public synchronized void show(){ if(num > 0){ try{ Thread. sleep(10); } catch(InterruptedException e){ e.printStackTrace(); } System. out.println(Thread.currentThread().getName() + "...function..." + num--); } } } class SynFunctionLockDemo{ public static void main(String[] args){ Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); try{ Thread. sleep(10); } catch(InterruptedException e){ e.printStackTrace(); } t. flag = false ; t2.start(); } }
静态的同步函数使用的锁是该函数所属字节码文件对象,可以用getClass方法获取,也可以用当前类名.class表示
class Ticket implements Runnable{ private static int num = 100; Object obj = new Object(); boolean flag = true; public void run(){ if(flag ){ while(true ){ synchronized(Ticket.class){//this.getClass() if(num > 0){ try{ Thread. sleep(10); } catch(InterruptedException e){ e.printStackTrace(); } System. out.println(Thread.currentThread().getName() + "...obj..." + num--); } } } } else while(true ) show(); } public static synchronized void show(){ if(num > 0){ try{ Thread. sleep(10); } catch(InterruptedException e){ e.printStackTrace(); } System. out.println(Thread.currentThread().getName() + "...function..." + num--); } } } class SynFunctionLockDemo{ public static void main(String[] args){ Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); try{ Thread. sleep(10); } catch(InterruptedException e){ e.printStackTrace(); } t. flag = false ; t2.start(); } }
多线程下的单例问题
饿汉式:
class Single{ private static final Single s = new Single(); private Single(){} public static Single getInstance(){ return s ; } }
饿汉式不存在安全问题,因为不存在多个线程共同操作数据的情况。
懒汉式:
class Single{ private static Single s = null; private Single(){} public static Single getInstance(){ if(s ==null){ synchronized(Single.class){ if(s == null) s = new Single(); } } return s ; } }
懒汉式存在安全问题,可以使用同步函数解决。
但若直接使用同步函数,则效率较低,因为每次都需要判断。
public static synchronized Single getInstance(){ If(s==null){ S == new Single() } }
但若采取如下方式,即可提升效率。
public static Single getInstance(){ if(s ==null){ synchronized(Single.class){ if(s == null) s = new Single(); } } return s ;
原因在于任何一个线程在执行到第一个if判断语句时,如果Single对象已经创建,则直接获取即可,而不用判断是否能够获取锁,相对于上面使用同步函数的方法就提升了效率。
如果当前线程发现Single对象尚未创建,则再判断是否能够获取锁。
死锁
死锁常见情景之一:同步的嵌套。
class Ticket implements Runnable{ private static int num = 100; Object obj = new Object(); boolean flag = true; public void run(){ if(flag ){ while(true ){ synchronized(obj ){ show(); } } } else while(true ) show(); } public synchronized void show(){ synchronized(obj ){ if(num > 0){ try{ Thread. sleep(10); } catch(InterruptedException e){ e.printStackTrace(); } System. out.println(Thread.currentThread().getName() + "...function..." + num--); } } } } class DeadLockDemo{ public static void main(String[] args){ Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); t1.start(); try{ Thread. sleep(10); } catch(InterruptedException e){ e.printStackTrace(); } t. flag = false ; t2.start(); } }结果:
例2
class Test implements Runnable{ private boolean flag ; Test( boolean flag){ this.flag = flag; } public void run(){ if(flag ){ while(true ) synchronized(MyLock.locka){ System. out.println(Thread.currentThread().getName() + "...if locka..."); synchronized(MyLock.lockb){ System. out.println(Thread.currentThread().getName() + "...if lockb..."); } } } else{ while(true ) synchronized(MyLock.lockb){ System. out.println(Thread.currentThread().getName() + "...else lockb..."); synchronized(MyLock.locka){ System. out.println(Thread.currentThread().getName() + "...else locka..."); } } } } } class MyLock{ public static final Object locka = new Object(); public static final Object lockb = new Object(); } class DeadLockTest{ public static void main(String[] args){ Test a = new Test(true ); Test b = new Test(false ); Thread t1 = new Thread(a); Thread t2 = new Thread(b); t1.start(); t2.start(); } }结果:
线程间通信
线程间通信涉及的方法
多个线程在处理统一资源,但是任务却不同,这时候就需要线程间通信。
等待/唤醒机制涉及的方法:
① wait():让线程处于冻结状态,被wait的线程会被存储到线程池中。
② notify():唤醒线程池中的一个线程(任何一个都有可能)。
③ notifyAll():唤醒线程池中的所有线程。
注意:
1、这些方法都必须定义在同步中,因为这些方法是用于操作线程状态的方法。
2、必须要明确到底操作的是哪个锁上的线程!
3、wait和sleep区别?
①wait可以指定时间也可以不指定。sleep必须指定时间。
②在同步中时,对CPU的执行权和锁的处理不同。
wait:释放执行权,释放锁。
sleep:释放执行权,不释放锁。
生产者-消费者问题:
class Resource{ private String name ; private String sex ; private boolean flag = false; public synchronized void set(String name,String sex){ if(flag ) try{ this.wait(); } catch(InterruptedException e){ e.printStackTrace(); } this.name = name; this.sex = sex; flag = true ; this.notify(); } public synchronized void out(){ if(!flag ) try{ this.wait(); } catch(InterruptedException e){ e.printStackTrace(); } System. out.println(name + "..." + sex); flag = false ; this.notify(); } } //输入 class Input implements Runnable{ Resource r; Input(Resource r){ this.r = r; } public void run(){ int x = 0; while(true ){ if(x == 0){ r.set( "mike","男" ); } else{ r.set( "lili","女" ); } x = (x + 1)%2; } } } //输出 class Output implements Runnable{ Resource r; Output(Resource r){ this.r = r; } public void run(){ while(true ){ r.out(); } } } class ResourceDemo { public static void main(String[] args){ //创建资源 Resource r = new Resource(); //创建任务 Input in = new Input(r); Output out = new Output(r); //创建线程,执行路径 Thread t1 = new Thread(in); Thread t2 = new Thread(out); //开启线程 t1.start(); t2.start(); }结果:
多生产者多消费者问题:
class Resource{ private String name ; private int count = 1; private boolean flag = false; public synchronized void set(String name){ if(flag ) try{ wait(); } catch(InterruptedException e){ e.printStackTrace(); } this.name = name + count; count++; System. out.println(Thread.currentThread().getName() + "...生产者..." + this. name); flag = true ; notify(); } public synchronized void out(){ if(!flag ) try{ wait(); } catch(InterruptedException e){ e.printStackTrace(); } flag = false ; notify(); System. out.println(Thread.currentThread().getName() + "...消费者..." + this. name); } } class Producer implements Runnable{ private Resource r ; Producer(Resource r){ this.r = r; } public void run(){ while(true ){ r.set( "烤鸭"); } } } class Consumer implements Runnable{ private Resource r ; Consumer(Resource r){ this.r = r; } public void run(){ while(true ){ r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args){ Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
由于if判断标记,只有一次,会导致不该运行的线程运行了,出现了数据错误的情况。故修改成while判断标记,线程获取CPU执行权及锁后,将重新判断是否具备运行条件。
notify方法只能唤醒一个线程,如果本方唤醒了本方,没有意义。而且while判断标记+notify会导致死锁。notifyAll解决了本方线程一定会唤醒对方线程的问题。
class Resource{ private String name ; private int count = 1; private boolean flag = false; public synchronized void set(String name){ while(flag ) try{ this.wait(); } catch(InterruptedException e){ e.printStackTrace(); } this.name = name + count; count++; System. out.println(Thread.currentThread().getName() + "...生产者..." + this. name); flag = true ; notifyAll(); } public synchronized void out(){ while(!flag ) try{ this.wait(); } catch(InterruptedException e){ e.printStackTrace(); } flag = false ; notifyAll(); System. out.println(Thread.currentThread().getName() + "...消费者..." + this. name); } } class Producer implements Runnable{ private Resource r ; Producer(Resource r){ this.r = r; } public void run(){ while(true ){ r.set( "烤鸭"); } } } class Consumer implements Runnable{ private Resource r ; Consumer(Resource r){ this.r = r; } public void run(){ while(true ){ r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args){ Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }结果:
JDK1.5新特性-Lock、Condition
同步代码块就是对于锁的操作是隐式的。
JDK1.5以后将同步和锁封装成了对象,并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。
Lock接口:出现替代了同步代码块或者同步函数,将同步的隐式操作变成显示锁操作。同时更为灵活,可以一个锁上加上多组监视器。
lock():获取锁。
unlock():释放锁,为了防止异常出现,导致锁无法被关闭,所以锁的关闭动作要放在finally中。
Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象,可以任意锁进行组合。
Condition接口中的await方法对应于Object中的wait方法。
Condition接口中的signal方法对应于Object中的notify方法。
Condition接口中的signalAll方法对应于Object中的notifyAll方法。
<pre name="code" class="java">class Resource{ private String name ; private int count = 1; private boolean flag = false; //创建一个锁对象 Lock lock = new ReentrantLock(); //通过已有的锁获取该锁上的监视器对象 Condition con = lock .newCondition(); public void set(String name){ lock.lock(); try{ while(flag ) try{ con.await(); } catch(InterruptedException e){ e.printStackTrace(); } this.name = name + count; count++; System. out.println(Thread.currentThread().getName() + "...生产者..." + this. name); flag = true ; con.signalAll(); }finally{ lock.unlock(); } } public void out(){ lock.lock(); try{ while(!flag ) try{ con.await(); } catch(InterruptedException e){ e.printStackTrace(); } flag = false ; con.signalAll(); System. out.println(Thread.currentThread().getName() + "...消费者..." + this. name); }finally{ lock.unlock(); } } } class Producer implements Runnable{ private Resource r ; Producer(Resource r){ this.r = r; } public void run(){ while(true ){ r.set( "烤鸭"); } } } class Consumer implements Runnable{ private Resource r ; Consumer(Resource r){ this.r = r; } public void run(){ while(true ){ r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args){ Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }结果:
使用一个Lock、两个Condition修改上面的多生产者-多消费者问题:
class Resource{ private String name ; private int count = 1; private boolean flag = false; //创建一个锁对象 Lock lock = new ReentrantLock(); //通过已有的锁获取该锁上的监视器对象 Condition con = lock .newCondition(); //通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者 Condition producer_con = lock .newCondition(); Condition consumer_con = lock .newCondition(); public void set(String name){ lock.lock(); try{ while(flag ) try{ producer_con.await(); } catch(InterruptedException e){ e.printStackTrace(); } this.name = name + count; count++; System. out.println(Thread.currentThread().getName() + "...生产者..." + this. name); flag = true ; consumer_con.signal(); } finally{ lock.unlock(); } } public void out(){ lock.lock(); try{ while(!flag ) try{ consumer_con.await(); } catch(InterruptedException e){ e.printStackTrace(); } flag = false ; producer_con.signal(); System. out.println(Thread.currentThread().getName() + "...消费者..." + this. name); } finally{ lock.unlock(); } } } class Producer implements Runnable{ private Resource r ; Producer(Resource r){ this.r = r; } public void run(){ while(true ){ r.set( "烤鸭"); } } } class Consumer implements Runnable{ private Resource r ; Consumer(Resource r){ this.r = r; } public void run(){ while(true ){ r.out(); } } } class ProducerConsumerDemo { public static void main(String[] args){ Resource r = new Resource(); Producer pro = new Producer(r); Consumer con = new Consumer(r); Thread t0 = new Thread(pro); Thread t1 = new Thread(pro); Thread t2 = new Thread(con); Thread t3 = new Thread(con); t0.start(); t1.start(); t2.start(); t3.start(); } }
停止线程
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常就用定义标记来完成。
class StopThread implements Runnable{ private boolean flag = true; public void run(){ while(flag ){ System. out.println(Thread.currentThread().getName() + "..."); } } public void setFlag(){ 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.start(); t2.start(); int num = 1; for(;;){ if(++num == 50){ st.setFlag(); break; } System. out.println("main..." + num); } System. out.println("over" ); } }
也可以使用stop方法停止线程,不过已经过时,不再使用。
但是如果线程处于了冻结状态,无法读取标记,如何结束呢?
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备CPU的执行资格。强制动作会发生InterruptedException,一定要记得处理。
相关文章推荐
- 黑马程序员学习笔记七——多线程
- 黑马程序员------java学习笔记之多线程
- 黑马程序员---java学习笔记之多线程2
- 黑马程序员_多线程的线程间通信学习笔记
- 黑马程序员 java学习笔记——多线程1
- 黑马程序员_多线程的创建和运行机制学习笔记
- 黑马程序员_学习笔记:7) 多线程、同步
- 黑马程序员—学习笔记之C#多线程
- [黑马程序员]多线程_学习笔记9
- 黑马程序员 JAVA学习笔记 ——— 多线程
- 黑马程序员_java多线程学习笔记
- 黑马程序员java学习笔记之四(java多线程总结)
- 黑马程序员---java学习笔记之多线程
- 黑马程序员 学习笔记二:多线程之Runnable接口
- 黑马程序员—Java基础学习笔记之多线程
- 黑马程序员java学习笔记,多线程
- 黑马程序员---Java基础学习笔记(多线程-后篇)
- 黑马程序员_学习笔记第12天——多线程2
- 黑马程序员-java学习笔记-多线程
- 黑马程序员_学习笔记第11天——多线程1