-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
多线程
进程与线程:
进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。一个进程至少有一个线程。
线程:就是进程中的一个独立的控制单元。 线程在控制着进程的执行。
Java VM 启动的时候会有一个进程java.exe。该进程中至少一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。
该线程称之为主线程。扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。
多线程程序运行时多个线程都在都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。
这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。
如何在程序中自定义线程呢?
Java给我们提供了对象线程这类事物的描述。该类是Thread;该类中定义了,创建线程对象的方法(构造函数)。
提供了要被线程执行的代码存储的位置(run()),还定义了开启线程运行的方法(start())。
同时还有一些其他的方法用于操作线程:static Thread currentThead():获取当前线程对象
String getName(): 获取线程名称
static void sleep(time)throws InterruptedException:让线程从运行状态进入休眠状态
创建线程的第一种方式:继承Thread类;
步骤:
1,定义类继承Thread。2,复写Thread类中的run方法。 目的:将自定义代码存储在run方法。让线程运行。
3,创建Thread类的子类对象,其实就是在创建线程,调用start方法。
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); } }
创建线程的第二种方式:实现Runnable接口;
步骤:
1,定义了实现Runnable接口。2,覆盖接口的run方法。将多线程要运行的代码存入其中。
3,创建Thread类的对象(创建线程),并将Runnable接口的子类对象作为参数传递给Thread的构造函数。
为什么要传递?因为线程要运行的代码都在Runnable子类的run方法中存储。所以要将该run方法所属的对象传递给Thread。让Thread线程去使用该对象调用其run方法。
4,调用Thread对象的start方法。开启线程。
为什么要覆盖run方法呢?
Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。也就是说Thread类中的run方法,用于存储线程要运行的代
码。
两种创建线程方法的区别:
实现方式好处:避免了单继承的局限性。在定义线程时,建立使用实现方式。
两种方式区别:
继承Thread:线程代码存放在Thread子类run方法中。
实现Runnable:线程代码存放在接口的子类的run方法中。
线程的状态:
1,被创建。
2,运行。
3,冻结。
4,消亡。其实还有一种特殊的状态:临时状态。该临时状态的特点:具备了执行资格,但不具备执行权。
冻结状态的特点:放弃了执行资格。如下图:
线程的安全问题:
问题的产生的原因:
几个关键点:
1,多线程代码中有操作共享数据。
2,多条语句操作该共享数据。
当具备两个关键点时,
有一个线程对多条操作共享数据的代码执行的一部分。还没有执行完,另一个线程开始参与执行。就会发生数据错误。
解决方法:Java对于多线程的安全问题提供了专业的解决方式-----同步,当一个线程在执行多条操作共享数据代码时,其他线程即使获取了执行权,也不可以参与操作。
同步代码块。
synchronized(对象)
{
需要被同步的代码}
如以下代码:
class Ticket implements Runnable { private int tick = 1000; Object obj = new Object(); public void run() { while(true) { synchronized(obj) { if(tick>0) { //try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--); } } } } } class TicketDemo2 { 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(); } }
对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。除了同步代码块之外,还有另外一种解决方式,就是同步函数。 在函数上加上synchronized修饰符即可。
同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。用以下程序验证一下:
class Ticket implements Runnable { private int tick = 100; Object obj = new Object(); boolean flag = true; public void run() { if(flag) { while(true) { synchronized(this) { if(tick>0) { try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"....code : "+ tick--); } } } } else while(true) show(); } public synchronized void show()//this { if(tick>0) { try{Thread.sleep(10);}catch(Exception e){} System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--); } } } class ThisLockDemo { 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(Exception e){} t.flag = false; t2.start(); } }
注意:对于static的同步函数,使用的锁不是this。是 类名.class 是该类的字节码文件对象。如以下经典示例,单例设计模式,懒汉式:
class Single { private static Single s = null; private Single(){} public static Single getInstance() { if(s==null) { synchronized(Single.class) { if(s==null) //--->A; s = new Single(); } } return s; } }
同步的前提:1,必须要有两个或者两个以上的线程。
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 { 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(); } }
线程间的通讯:
其实就是多个线程在操作同一个资源,但是操作的动作不同。如以下示例:
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(pro); Thread t3 = new Thread(con); Thread t4 = new Thread(con); t1.start(); t2.start(); t3.start(); t4.start(); } } 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(Exception e){} this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag = true; this.notifyAll();//唤醒对方线程。如果只用notify,容易出现只唤醒本方线程的情况。导致程序中的所有线程都等待。 } public synchronized void out() { while(!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(); } } }
JDK1.5中提供了多线程升级解决方案。将同步synchronized替换成显示的Lock操作。将Object中wait,notify,notifyAll,替换成了Condition对象。该Condition对象可以通过Lock锁进行获取,并支持多个
相关的Condition对象。升级解决方案的示例:
<pre class="java" name="code">import java.util.concurrent.locks.*; class ProducerConsumerDemo2 { 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(pro); Thread t3 = new Thread(con); Thread t4 = new Thread(con); t1.start(); t2.start(); t3.start(); t4.start(); } } class Resource { private String name; private int count = 1; private boolean flag = false; private Lock lock = new ReentrantLock(); private Condition condition_pro = lock.newCondition(); private Condition condition_con = lock.newCondition(); public void set(String name)throws InterruptedException { lock.lock(); try { while(flag) condition_pro.await(); this.name = name+"--"+count++; System.out.println(Thread.currentThread().getName()+"...生产者.."+this.name); flag = true; condition_con.signal(); } finally { lock.unlock();//释放锁的动作一定要执行。 } } public void out()throws InterruptedException { lock.lock(); try { while(!flag) condition_con.await(); System.out.println(Thread.currentThread().getName()+"...消费者........."+this.name); flag = false; condition_pro.signal(); } finally { lock.unlock(); } } } class Producer implements Runnable { private Resource res; Producer(Resource res) { this.res = res; } public void run() { while(true) { try { res.set("+商品+"); } catch (InterruptedException e) { } } } } class Consumer implements Runnable { private Resource res; Consumer(Resource res) { this.res = res; } public void run() { while(true) { try { res.out(); } catch (InterruptedException e) { } } } }
停止线程:
如何停止线程?
只有一种,run方法结束。开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。强制让线程恢复到运行状态
中来。这样就可以操作标记让线程结束。Thread类提供该方法 interrupt()。
扩展小知识:
join()方法;当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。
setPriority()方法用来设置优先级。
MAX_PRIORITY 最高优先级10
MIN_PRIORITY 最低优先级1
NORM_PRIORITY 分配给线程的默认优先级
yield()方法可以暂停当前线程,让其他线程执行。
-----------android培训、java培训、java学习型技术博客、期待与您交流!------------