黑马程序员_Java_多线程
2015-01-22 18:19
183 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
一、多线程概述
1、进程、线程与多线程
进程是一个正在执行的程序,每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。比如虚拟机启动时有一个java.exe的执行程序就是一个进程。
线程属于某个进程,不能自动运行,要由进程启动执行,进行控制。线程是进程中的一个独立的控制单元,线程在控制着进程的执行,一个进程中至少有一个线程。每个独立线程代表一个独立操作。
一个进程中有多个线程,称为多线程。虚拟机启动的时候就是多线程,JVM 启动不止一个线程,至少有:一个主线程和一个负责垃圾回收的线程。多线程提高了程序的运行效率,但线程太多会导致效率的降低,因为线程的执行依靠的是CPU的来回切换。
二、线程的创建方式
线程有两种创建方式,分别是继承Thread类和实现Runnable接口。
2.1 继承Thread类的创建步骤:
⑴定义类继承Thread类;
⑵复写Thread中的run方法,用于存储线程要执行的代码;
⑶调用线程的start方法启动线程。
代码示例:
2.2 实现Runnable接口的创建步骤:
⑴定义类实现Runnable接口;
⑵复写Runnable接口中的run方法,用于存储线程要执行的代码;
⑶创建自定义的Runnable的子类对象;
⑷将Runnable接口的子类对象作为参数传给Thread类的构造函数;
⑸调用Thread类的start方法开启线程。
代码示例:
2.3 用匿名内部类实现两种线程:
用匿名内部类继承Thread类和实现Runnable接口,方便代码的书写。
代码示例:
两种创建方式的区别:
继承Thread类:线程代码存放于Thread子类的run方法中,无法继承其他类。
实现Runnable接口:线程代码存放于Runnable子类的run方法中,避免了单继承的局限性,定义线程时,推荐使用实现的方法。
三、 Thread类常用方法
⑴获取当前线程对象,通过currentThread()方法获取当前线程对象
⑵获取线程名字,通过getName()方法获取线程对象的名字
⑻setPriority()设置线程的优先级,加大线程CPU运行几率。
四、多线程的安全问题
多线程的运行出现安全问题的原因:当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:对多条操作共享数据的余军,只能让一个线程执行完。在执行过程中,其他线程不可以参与执行。java提供了专业的解决方法:同步代码块和同步函数。
同步代码块使用的锁是对象,其结构为:
同步的前提:必须要有两个或两个以上的线程;必须是多个线程使用同一个锁;必须保证同步中只能有一个线程在运行。
同步解决了多线程的安全问题,但多个线程需要判断锁,较为消耗资源。
以下为同步代码块和同步函数嵌套的代码示例:
五、死锁
同步中嵌套同步,使用了互斥锁,造成互相等待,应避免使用不同锁。
代码示例:
六、线程间通讯
线程间通讯是多个线程在操作同一资源,但操作的动作不同。
1、早期等待唤醒的线程间通讯方式
wait():在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待。换句话说,此方法的行为就好像它执行wait(0)调用一样。
notify():唤醒在此对象监视器上等待的单个线程。
notifyAll():唤醒在此对象监视器上等待的所有线程。
下面为一个输出交替变化的线程间通讯代码示例:
使用Lock替代synchronized同步,使用Condition对象的await()、single()、singleAll()方法替代Object中的wait()、notify()、notifyAll()方法。
代码示例:
一、多线程概述
1、进程、线程与多线程
进程是一个正在执行的程序,每一个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。比如虚拟机启动时有一个java.exe的执行程序就是一个进程。
线程属于某个进程,不能自动运行,要由进程启动执行,进行控制。线程是进程中的一个独立的控制单元,线程在控制着进程的执行,一个进程中至少有一个线程。每个独立线程代表一个独立操作。
一个进程中有多个线程,称为多线程。虚拟机启动的时候就是多线程,JVM 启动不止一个线程,至少有:一个主线程和一个负责垃圾回收的线程。多线程提高了程序的运行效率,但线程太多会导致效率的降低,因为线程的执行依靠的是CPU的来回切换。
二、线程的创建方式
线程有两种创建方式,分别是继承Thread类和实现Runnable接口。
2.1 继承Thread类的创建步骤:
⑴定义类继承Thread类;
⑵复写Thread中的run方法,用于存储线程要执行的代码;
⑶调用线程的start方法启动线程。
代码示例:
<span style="font-size:12px;">package demo.thread; public class ThreadDemo { public static void main(String[] args) { Demo demo=new Demo(); demo.start();//3,调用线程的start方法启动线程 } } class Demo extends Thread{//1,定义类继承Thread public void run() {//2,复写Thread中的run方法,用于存储线程要执行的代码 System.out.println("demo run"); System.out.println(this.getName()); return; } }</span>
2.2 实现Runnable接口的创建步骤:
⑴定义类实现Runnable接口;
⑵复写Runnable接口中的run方法,用于存储线程要执行的代码;
⑶创建自定义的Runnable的子类对象;
⑷将Runnable接口的子类对象作为参数传给Thread类的构造函数;
⑸调用Thread类的start方法开启线程。
代码示例:
<span style="font-size:12px;">package demo.thread; public class ThreadDemo { public static void main(String[] args) { Demo demo=new Demo();//3,创建自定义的Runnable的子类对象; Thread t=new Thread(demo);//4,将Runnable接口的子类对象作为参数传给Thread类的构造函数; t.start();//5,调用Thread类的start方法开启线程。 } } class Demo implements Runnable{//1,定义类实现Runnable接口; public void run() {//2,复写Runnable接口中的run方法,用于存储线程要执行的代码; System.out.println("demo run"); return; } }</span>
2.3 用匿名内部类实现两种线程:
用匿名内部类继承Thread类和实现Runnable接口,方便代码的书写。
代码示例:
<span style="font-size:12px;">package demo.thread; public class ThreadTestDemo { public static void main(String[] args) { new Thread(){//匿名内部类继承Thread类 public void run(){//复写Thread中的run方法,用于存储线程要执行的代码; for(int x=0;x<100;x++){ System.out.print(Thread.currentThread().getName()+",..."+x); } } }.start();//调用线程的start方法启动线程。 //定义类实现Runnable接口; new Thread(/*创建自定义的Runnable的子类对象;将Runnable接口的子类对象作为参数传给Thread类的构造函数*/ new Runnable(){ public void run() {//复写Runnable接口中的run方法,用于存储线程要执行的代码; for(int x=0;x<100;x++){ System.out.print(Thread.currentThread().getName()+",..."+x); } } }).start();//调用Thread类的start方法开启线程。 for(int x=0;x<100;x++){ System.out.print(Thread.currentThread().getName()+",..."+x); } } } </span>
两种创建方式的区别:
继承Thread类:线程代码存放于Thread子类的run方法中,无法继承其他类。
实现Runnable接口:线程代码存放于Runnable子类的run方法中,避免了单继承的局限性,定义线程时,推荐使用实现的方法。
三、 Thread类常用方法
⑴获取当前线程对象,通过currentThread()方法获取当前线程对象
<span style="font-size:12px;"> System.out.print(Thread.currentThread());</span>
⑵获取线程名字,通过getName()方法获取线程对象的名字
<span style="font-size:12px;"> System.out.print(Thread.currentThread().getName()); </span>⑶设置线程名字,通过setName()方法获取线程对象的名字
<span style="font-size:12px;"> Thread.currentThread().setName("Java多线程");</span>⑷休眠,sleep()方法
<span style="font-size:12px;"> Thread.sleep(10);//休眠10毫秒</span>⑸守护setDaemon(),设置一个线程为守护线程,该线程不会单独执行,当其他非守护线程都结束后自动退出
<span style="font-size:12px;"> Thread.setDaemon(true);//设置为守护线程</span>⑹加入join(),当前线程暂停,等待指定的线程执行结束后,当前线程再继续
<span style="font-size:12px;"> Thread.join();//等待该线程结束后,当前线程继续执行</span>⑺yield()让出CPU,暂停当前正在执行的线程对象,并执行其他线程。
<span style="font-size:12px;"> Thread.yield();//<span style="white-space: pre;">暂停当前正在执行的线程对象,并执行其他线程</span></span>
⑻setPriority()设置线程的优先级,加大线程CPU运行几率。
<span style="font-size:12px;"> Thread.setPriority(Thread.MAX_PRIORITY);//设置最高优先级</span>
四、多线程的安全问题
多线程的运行出现安全问题的原因:当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:对多条操作共享数据的余军,只能让一个线程执行完。在执行过程中,其他线程不可以参与执行。java提供了专业的解决方法:同步代码块和同步函数。
同步代码块使用的锁是对象,其结构为:
<span style="font-size:12px;"> synchronized(对象){ //被同步的代码 }</span>非静态同步函数使用的锁是this,静态函数使用的锁是类.class,其结构为:
<span style="font-size:12px;"> public (static) synchronized void 函数(){ //被同步的代码 }</span>
同步的前提:必须要有两个或两个以上的线程;必须是多个线程使用同一个锁;必须保证同步中只能有一个线程在运行。
同步解决了多线程的安全问题,但多个线程需要判断锁,较为消耗资源。
以下为同步代码块和同步函数嵌套的代码示例:
<span style="font-size:12px;">package demo.thread; public class TicketDemo { public static void main(String[] args) { // TODO Auto-generated method stub TickedThread p=new TickedThread(); Thread t1=new Thread(p);//两个线程购票 Thread t2=new Thread(p); t1.start(); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } p.flag=false; t2.start(); } } //购票实例 class TickedThread implements Runnable{//extends Thread{ private static int tick=300;//300张票 boolean flag=true; Object mutex=new Object(); public void run() { if(flag) while(true) synchronized (mutex) {//同步代码块 buy();//同步函数 } else while(true) buy(); } public synchronized void buy(){//同步函数 synchronized (mutex) {//同步代码快 if(tick>0) System.out.println(Thread.currentThread()+"code :"+tick--); } } }</span>
五、死锁
同步中嵌套同步,使用了互斥锁,造成互相等待,应避免使用不同锁。
代码示例:
<span style="font-size:12px;">package demo.thread; //死锁实例 public class DeadLockDemo { public static void main(String[] args) { // TODO Auto-generated method stub Thread t1=new Thread(new Test(true)); Thread t2=new Thread(new Test(false)); t1.start(); t2.start();//使用不同锁,容易造成死锁 } } class Test implements Runnable{ private boolean flag; Test(boolean flag){ this.flag=flag; } public void run(){ if(flag){ synchronized(MyLock.locka){//使用了锁A System.out.println("if locka"); synchronized(MyLock.lockb){ System.out.println("if lockb"); } } }else{ synchronized(MyLock.lockb){//使用了锁B System.out.print("else lockb"); synchronized(MyLock.locka){ System.out.print("else locka"); } } } } } class MyLock{ static Object locka=new Object(); static Object lockb=new Object(); }</span>
六、线程间通讯
线程间通讯是多个线程在操作同一资源,但操作的动作不同。
1、早期等待唤醒的线程间通讯方式
wait():在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待。换句话说,此方法的行为就好像它执行wait(0)调用一样。
notify():唤醒在此对象监视器上等待的单个线程。
notifyAll():唤醒在此对象监视器上等待的所有线程。
下面为一个输出交替变化的线程间通讯代码示例:
<span style="font-size:12px;">package demo.thread; //线程间通讯,输入输出交替变化 public class InputOutputDemo { public static void main(String[] args) { Res r=new Res(); new Thread(new Input(r)).start(); new Thread(new Output(r)).start(); } } class Res{//同一操作资源 private String name; private String sex; private boolean flag=false; public synchronized void set(String name,String sex){//同步函数设置姓名性别 if(flag) try { wait();//等待 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.name=name; this.sex=sex; flag=true; this.notify();//唤醒 } public synchronized void out(){//输出姓名性别 if(!flag){ try { wait();//等待 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(name+"--------"+sex); flag=false; this.notify();//唤醒 } } class Input implements Runnable{ Res r; Input(Res r){ this.r=r; } public void run() { // TODO Auto-generated method stub int x=0; while(true){ if(x==0) r.set("mike","man"); else r.set("lili","woman"); x=(x+1)%2;//名字性别交替变化 } } } class Output implements Runnable{ Res r; Output(Res r){ this.r=r; } @Override public void run() { // TODO Auto-generated method stub while(true){ r.out(); } } }</span>2、JDk1.5后提供了等待唤醒升级方案:
使用Lock替代synchronized同步,使用Condition对象的await()、single()、singleAll()方法替代Object中的wait()、notify()、notifyAll()方法。
代码示例:
<span style="font-size:12px;">package demo.thread; //生产者和消费者实例 import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ProducerConsumerDemo { public static void main(String[] args) { // TODO Auto-generated method stub 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 Resource{ private String name; private int count=1; private boolean flag=false; private Lock lock=new ReentrantLock(); private Condition con=lock.newCondition(); public void set(String name){ lock.lock();//锁住 try {while(flag) con.await();//等待 } catch (InterruptedException e) { e.printStackTrace(); } finally{ this.name=name+"--"+count++; System.out.println(Thread.currentThread().getName() +"...生产者"+this.name); flag=true; con.signalAll();//唤醒所有等待线程 lock.unlock();//解锁 } } public void out(){ lock.lock();//锁住 try {while(!flag) con.await();<span style="font-family: Arial, Helvetica, sans-serif;">//等待</span> } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally{ System.out.println(Thread.currentThread().getName() +"...消费者"+this.name); flag=false; con.signalAll();//唤醒所有等待线程 lock.unlock();//解锁 } } } class Producer implements Runnable{//生产者 private Resource res; Producer(Resource res){ this.res=res; } public void run(){ while(true){ res.set("+商品+"); } } } class Consumer implements Runnable{//消费者 private Resource res; Consumer(Resource res){ this.res=res; } public void run(){ while(true){ res.out(); } } }</span>
相关文章推荐
- 黑马程序员Java培训、Android培训-Java 学习过程记录_多线程3
- 黑马程序员入学篇——(7)java的多线程(续写)
- 黑马程序员---java多线程 学习笔记
- 黑马程序员_java基础加强10_多线程加强_工具类简介
- 黑马程序员_java基础加强9_多线程加强
- 黑马程序员Java培训、Android培训-Java 学习过程记录_多线程
- 黑马程序员---------笔记整理(java基础八-----多线程)
- 黑马程序员java学习笔记之四(java多线程总结)
- 黑马程序员_王康 java多线程
- 黑马程序员—java多线程
- 黑马程序员 (1) 对JAVA多线程编程进行学习。
- 黑马程序员-java多线程
- 黑马程序员_java多线程
- 黑马程序员_Java包和多线程
- 黑马程序员 _Java中的进程、线程和多线程
- 黑马程序员入学篇——(6)java的多线程
- 黑马程序员0831_java基础知识+多线程部分
- 黑马程序员-----java多线程的学习
- 黑马程序员_java基础加强7_多线程加强
- 黑马程序员--java多线程