java_多线程
来源:互联网 发布:星际淘宝网txt免费下载 编辑:程序博客网 时间:2024/05/17 08:11
1,多线程概念及相关对象
1.1多线程概念
(1)进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元
(2)线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行。
(3)多线程,多个线程共同执行一段代码,已达到提高效率,比如,一个浏览器必须能同时下载多个图片;一个Web服务器必须能同时响应多个用户请求;等都需要使用多线程技术来完成。
java已经提供了对线程这类事物的描述。就是thread类。
1.2,多线程的优势
(1)进程之间不能共享内存,但是线程之间共享内存非常容易。
(2)系统创建进程时需要为该进程重新非配系统资源,但创建线程则代价小的多,因此使用多线程来实现多任务并发比多进程的效率高。
(3)java语言内置了多线程功能支持,而不是单纯地作为底层操作系统的调度方式,从而简化了java的多线程编程。
1.3计算机CPU的运行原理
当我们电脑上有很多的程序在运行时会让人感觉是同时进行,但是在一个时刻,单核的cpu只能运行一个程序。而我们看到的同时运行效果,只是cpu在多个进程间做着快速切换动作。
而cpu执行哪个程序,是毫无规律性的。这也是多线程的一个特性:随机性。哪个线程抢到了cpu的执行权,哪个线程就执行。而cpu不会一直执行这个程序到结束,而是执行一个一会后,又会去执行另一个,而调度的方法是由系统决定的。
1.4线程的创建
(1)创建线程的第一种方式:继承Thread类。
步骤:
1,定义类继承Thread类
2,复写Thread类中的run方法。
目的:将自定义的代码存储在run方法中,让线程运行。
3,调用线程的start方法,
该方法两个作用:启动线程,调用run方法。
问:为什么要覆盖run方法呢?
答:Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
也就是说Thread类中的run方法,用于存储线程要运行的代码。(Thread类里的run()方法的方法体是空的,所以想要执行自己指定的代码就得覆盖父类的run()方法)
实例:
class FirstThread extends Thread{public void run(){for(int x=0;x<50;x++){//当线程继承Thread类时,直接使用this即可获得当前线程//Thread对象的getName()返回当前线程的名字System.out.println(getName()+".."+i);//System.out.println(Thread.currentThread().getName()+"..."+i);}}}class ThreadDemo{FirstThread tf = new FirstThread();//创建好一个线程。ft.start();//开启线程,并执行该线程的run方法。for(int x=0;x<50;x++){System.out.println(Thread.currentThread().getName()+"..."+i);}}
(2)创建线程的第二种方式:实现Runable接口
步骤:
1,定义类实现Runnable接口
2,覆盖Runnable接口中的run方法。
将线程要运行的代码存放在该run方法中。
3,通过Thread类建立线程对象。
4,将Runnable接口中的子类对象作为实际参数传递给Thread类的构造函数。
问:为什么要将Runnable接口的子类对象传递给Thread的构造函数
答:因为自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去执行指定对象的run方法。就必须明确该run方法所属的对象。
5,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
实例:
class RunnableThread implements Runnable{public void run(){for(int x=0;x<50;x++){//当线程实现Runnable接口时//如果想要获取当前线程,只能用Thread.currentThread()System.out.println(Thread.currentThread().getName()+"..."+i);}}}class ThreadDemo{RunnableThread rt = new RunnableThread();//下面两个线程共用rt这个资源new Thread(rt).start();new Thread(rt).start();//开启线程,并执行该线程的run方法。for(int x=0;x<50;x++){System.out.println(Thread.currentThread().getName()+"..."+i);}}
(3) 两种方式的对比:
1,采用实现Runnable接口的方式创建多线程
->线程类只是实现了Runnable接口,还可以继承其他类。
->在这种方式下,多个线程共享一个Runnable子类对象,所以非常适合多个相同线程来吹同一份资源的情况。
->劣势是:如果需要访问当前线程,则必须使用Thread.currentThread()方法。
2,采用继承Thread类的方式
->因为线程类已经继承了Thread类,所以不能再继承其他类父类。
->如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可获得当前线程。
1.5线程运行状态
被创建:等待启动,调用start启动。
运行状态:具有执行资格和执行权。
阻塞状态:有执行资格,但是没有执行权(没有处理器资源)。
冻结状态:遇到sleep(time)方法和wait()方法时,失去执行资格和执行权,sleep方法时间到或者调用notify()方法时,获得执行资格,变为阻塞状态。
消忙状态:stop()方法,或者run方法结束。
2,线程同步
2.1线程安全问题
2.1.1引发安全问题的原因
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分还没有执行完,
另一个线程参与进来执行。导致了共享数据的错误。
2.2线程同步问题
2.2.1,同步代码块
JAVA对于多线程的安全问题提供了专业的解决方式。
1,同步代码块。
synchronized(obj)
{
需要被同步的代码。//哪些代码需要同步?看哪些语句在操作共享数据。
}
2)obj(对象)如同锁(监视器)
持有锁的线程可以在同步中执行。没有持有锁的线程即使夺取了cpu的执行权,也进不去。因为没有获取锁
3)obj(对象)的选定
虽然java程序允许使用任何对象作为同步监视器,但想一下同步监视器的目的:阻止两个线程对同一个共享资源进行并发访问,
因此通常使用可能被并发访问的共享资源充当同步监视器。
4)好处和弊端
好处:解决多线程的安全问题。
弊端:多个线程都需要判断锁,较为消耗资源。
2.2.2,同步函数
与同步代码块对应,java的多线程安全支持还提供了同步方法,同步方法就是使用synchronized关键字来修饰某个方法,则该方法称为同步方法。
对于同步方法而言,无须显式指定同步监视器,同步方法的同步监视器是this,也就是该对象本身。
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class
2.2.3,同步的前提
1,必须有两个或者两个以上的线程。(两个人以上才锁门)
2,必须是多个线程使用同一个锁。
如果加了同步还出现安全问题就要考虑到两个前提是否都满足。
2.2.4,如何寻找多线程中的安全问题
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
2.2.5,利用同步的知识实现单例模式的懒汉式
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;}}
2.3,死锁
2.3.1原理:
一个同步里面嵌套一个同步,锁不一样。
2.3.2说明:
有两个程序,A持有一个锁,B也持有一个锁,A要到B里面去运行,要拿到B的锁,而B要到A里面运行,也要拿到A的锁
互相不释放自己的锁,又得要进到对方程序里运行就产生了死锁2.3.3死锁例子:
class Test implements Runnable{private boolean flag;Test(boolean flag){this.flag = flag;}public void run(){if(flag){synchronized(MyLock.locka){System.out.println("if locka");synchronized(MyLock.lockb){System.out.println("if lockb");}}}else{synchronized(MyLock.lockb){System.out.println("else lockb");synchronized(MyLock.locka){System.out.println("else locka");}}}}}class MyLock{static Object locka = new Object();static Object lockb = new Object(); }class DeadLockTest{public static void main(String[] args) {Thread t1 = new Thread(new Test(true));Thread t2 = new Thread(new Test(false));t1.start();t2.start();}}
2.4线程通信
2.4.1,方法
1,wait()
导致当前线程等待,直到其他线程调用该不同监视器的notify()或notifyAll()方法来唤醒该线程。
调用wait()方法的当前线程会释放对该同步监视器的锁。
2,notify()
唤醒在此同步监视器上等待的单个线程。
3,notifyAll()
唤醒在此同步监视器上等待的所有线程。
当多个线程进行存,多个进行进行取时,避免程序最后被阻塞无法继续向下执行。可以使用notifyAll()
但是notifyAll()方法是将所有等待线程唤醒,可不可只唤醒对方线程呢?
2.4.2,思考
1,问:为什么这些操作线程的方法要定义在Object的类中呢?
答:因为这3个方法需要同步监视器对象来调用,而同步监视器对象可以是任意对象。
2,问:wait()和sleep()有什么区别。
答:wait():释放资源,释放锁;sleep():释放资源,不释放锁。
2.4.3,多个线程在操作同一个资源,(一个线程在存,一个线程在取),
为了实现这个功能,可以借助于Object类提供的wait(),notify()和notifyAll()3个方法,这3个方法并不属于Thread类,而是属于Object类,但是这三个方法必须由同步监视器对象来调用。
实例:
class Res{ private String name;private String sex;private boolean flag = false;public synchronized void set(String name,String sex){if(flag)try{this.wait();}catch(Exception e){}this.name = name;this.sex = sex;flag = true;this.notify();}public synchronized void out() {if(!flag)try{this.wait();}catch(Exception e){}System.out.println(name+"...."+sex);flag = false;this.notify();}}class Input implements Runnable{private Res r;Input(Res r){this.r = r;}public void run(){int x = 0;while(true){if(x==0)r.set("mike","man");elser.set("丽丽","女女");x = (x+1)%2;//切换 }}}class Output implements Runnable{private Res r;Output(Res r){this.r = r;}public void run(){while(true){r.out();}}}class InputOutputDemo2{public static void main(String[] args) {Res r =new Res();new Thread(new Input(r)).start();new Thread(new Output(r)).start();}}
2.4.4,多个线程在操作同一个资源,(多个线程在生产,多个线程在消费)
class Resource{private String name;private int count = 1;private boolean flag = false;//资源空间必须都有标记。public synchronized void set(String name){ while(flag)//if(flag) 可能t2复活时没有判断标记,所以用while循环try{wait();}catch(Exception e){}this.name = name+"--"+count++;System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);flag = true;this.notifyAll();//用的是全部唤醒 ,能不能只唤醒对方。}public synchronized void out(){while(!flag)//if(!flag)try{wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"消费者"+this.name);flag = false;this.notifyAll();//用的是全部唤醒}}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();}}}class ProducerConsumerDemo {public static void main(String[] args) {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();}}
3,Lock类与Condition类
3.1Lock类
将同步Synchronized(隐视加锁,解锁) 替换成实现Lock操作(显示加锁,解锁)
( Lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作)
lock():获取锁。
unLock()释放锁。
3.2Condition类
如果程序不使用synchronized关键字来保证同步,而是直接使用Lock对象来保证同步,则系统不存在隐式的同步监视器,也就不能使用wait(),notify(),notifyAll()方法进行线程通信。
所以在使用Lock对象时,java提供了一个Condition类来保持协调。
Condition实例被绑定在一个Lock对象上,要获得特定Lock实例的Condition实例,调用Lock对象的newCondition()方法即可,
Condition替代了同步监视器的功能。Condition提供了3个方法await(),signal(),aignalAll()来替换wait()wait(),notify(),notifyAll().
具体使用方法:
class BoundedBuffer { final Lock lock = new ReentrantLock(); final Condition notFull = lock.newCondition(); final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count; public void put(Object x) throws InterruptedException { lock.lock(); try { while (count == items.length) notFull.await(); items[putptr] = x; if (++putptr == items.length) putptr = 0; ++count; notEmpty.signal(); } finally { lock.unlock(); } } public Object take() throws InterruptedException { lock.lock(); try { while (count == 0) notEmpty.await(); Object x = items[takeptr]; if (++takeptr == items.length) takeptr = 0; --count; notFull.signal(); return x; } finally { lock.unlock(); } } }
4,多线程其他方法
4.1,停止线程
stop方法已经过时。
suspend:会发生死锁
问:如何停止线程?
答:只有一种,run方法结束。开启多线程运行,运行代码通常都是循环结构。
只要控制循环,就可以让run方法结束,也就是线程结束。
但是,当线程处于冻结作态,就不会读取到标记,那么线程就不会结束。
所以,当没有指定的方式让冻结的线程回复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态中来,这样可以操作标记让线程结束。
Thread类提供该方法 interrupt();会产生InterruptedException.
步骤:
1,定义循环结束标记
因为线程运行代码一般都是循环,只要控制了循环即可。
2,使用interrupt(中断)方法(不正常唤醒,会发生异常)
该方法是结束线程的冻结状态(wait,sleep),使线程回到运行状态中来
4.2,后台线程
1)有一种线程,它是在后台运行的,它的任务是为其他的线程提供服务,这种线程被称为“后台线程”,又称为守护线程。JVM的垃圾回收线程就是典型的后台线程。
2)后台线程有一个特性:如果所有的前台线程都死亡,后台线程会自动死亡。
3)调用Thread对象的setDaemon(true)方法可将指定线程设置成后台线程。
4)setDaemon(true)必须在start()方法之前调用,否则会引发illegalThreadStateException异常。
4.3,join线程
Thread提供了让一个线程等待另一个线程完成的方法--join()方法。当在某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join方法加入的join线程执行完为止。
用法:join()方法通常由使用线程的程序调用,以将大问题划分成许多小问题,每一个小问题分配一个线程。当所有的小问题都得到处理后,再调用主线程来进一步操作。
4.4,线程让步:yield
yield()方法是一个和sleep()方法有点相似的方法,它也是Thread类提供的一个静态方法,它也可以让当前正在执行的线程暂停,但他不会阻塞该线程,它只是将该线程转入就绪状态。
yield()只是让当前的线程暂停一下,让系统的线程调度器重新调度一次,完全可能的情况是:当某个线程调用了yield()方法暂停之后,线程调度器又将其调度出来重新执行。
5,改变线程优先级
Thread提供了setPriority()方法来设置指定线程的优先级,其中参数可以是一个整数,1-10.也可以使用Thread类的如下3个静态常量。
->MAX_PRIORITY 10
->MIN_PRIORITY 1
->NORM_PRIORITY 5
- Java_多线程
- JAVA_多线程
- java_多线程
- java_多线程
- Java_多线程
- Java_多线程
- JAVA_多线程
- JAVA_多线程
- java_多线程
- java_多线程
- java_多线程死锁
- Java_多线程_简介
- JAVA_多线程讲解
- Java_多线程(上)
- Java_多线程(下)
- Java_多线程学习汇总
- Java_多线程基础
- java_多线程下载
- MFC双缓冲
- typeid操作符详解
- java中equal 和 ==的区别
- 数组名与指针区别
- TRIZ系列-创新原理-40-复合材料原理
- java_多线程
- C++初始化问题
- C语言第九天
- 我的新博客--nanxiao.me上线了
- c++ 学习笔记(47)-C++中两个类中互相包含对方对象的指针问题
- iOS工程如何支持64-bit
- C++ 派生类指针强制指向基类对象
- 第九周项目五程序填充题
- iOS 网络错误-分类