线程

来源:互联网 发布:php技术文档怎么写 编辑:程序博客网 时间:2024/05/16 20:28

一:线程和进程

线程是进程中的内容,每一个应用程序里面至少都有一个线程,每个线程都是一个执行路径.

线程存在的意义:可以使多部分代码同时进行

进程:是一个正在执行的程序,每个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元

线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行,一个进程中至少有一个线程

主线程:Java VM  启动的时候会有一个进程java.exe.该进程中至少一个线程负责java程序的执行。

而且这个线程运行的代码存在于main方法中。该线程称之为主线程。
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。
多线程的五个状态图:
二 线程的随机性:
发现运行结果每一次都不同。因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。

三 创建线程的两种方式:
实现Runnable接口和继承Thread类
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
两种方式区别:
继承Thread:线程代码存放Thread子类run方法中。实现Runnable,线程代码存在接口的子类的run方法。





1.继承Thread类:
步骤:
(1),定义类继承Thread。
(2),复写Thread类中的run方法。目的:将自定义代码存储在run方法。让线程运行。
(3),调用线程的start方法,该方法两个作用:启动线程,调用run方法。
代码:
public class ThreadCreateMethod {public static void main(String[] args) {thread1 t1 = new thread1();//创建一个线程//t1.run();//仅仅是对象调用方法。而线程创建了,并没有运行。(属于单线程程序,只有start才能开启线程)t1.start();//开启线程并执行该线程的run方法。for(int x=0; x<60; x++)System.out.println("Hello World!--"+x);}}class thread1 extends Thread{public void run(){for(int x = 0;x<60;x++){System.out.println("thread1...is run"+x);}}}

1.1例子:创建两个线程,和主线程交替进行
static Thread currentThread():获取当前线程对象
getName():获取线程的名称
设置线程的名称:setName或者构造函数
代码:
public class ThreadGetName {public static void main(String[] args) {thread2 t1 = new thread2("t1");thread2 t2 = new thread2("t2");t1.start();t2.start();for(int x=0;x<60;x++)System.out.println("main----------"+x);}}class thread2 extends Thread{thread2(String name){super(name);}public void run(){for(int x = 0;x<60;x++){System.out.println((Thread.currentThread()==this)+"..."+this.getName()+"thread2 is run"+x);}}}
1.2 覆盖run方法的原因:
Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。也就是说Thread类中的run方法,用于存储线程要运行的代码。

2.创建线程的第二种方式:实现Runable接口
步骤:
(1),定义类实现Runnable接口
(2),覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。

(3),通过Thread类建立线程对象。
(4),将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
(5),调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
示例:比如多个窗口同时卖票的例子:
public class TicketImplements {public static void main(String[] args) {//相当于4个窗口同时卖票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();}}class Ticket implements Runnable{//实现runnable接口private int ticket = 100;public void run() {while(true){if(ticket>0){System.out.println(Thread.currentThread().getName()+"...sale"+ticket--);}}}}
2.1但是通过上面的代码发现,打印出0,-1,-2等错票多线程的运行出现了安全问题
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行java对于多线程的安全问题提供了专业的解决方式,就是同步代码块

四 线程同步
1.同步代码块格式
synchronized(对象)
{
需要被同步的代码
}
对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
同步的前提:
(1).必须要有两个或者两个以上的线程。
(2).必须是多个线程使用同一个锁。必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题
弊端:多个线程都需要判断锁,较为消耗资源。
2.卖票的示例同步后:
public class TicketImplements {public static void main(String[] args) {//相当于4个窗口同时卖票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();}}class Ticket implements Runnable{//实现runnable接口private int ticket = 100;Object obj = new Object();public void run() {while(true){<span style="color:#ff0000;">synchronized(obj){if(ticket>0){System.out.println(Thread.currentThread().getName()+"...sale"+ticket--);}}</span>}}}
3.多线程-同步函数
3.1 非静态的同步函数:
通过一个银行实例来认识同步代码块:
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次。
如何找问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
public class BankDemo {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubCus c = new Cus();Thread t1 = new Thread(c);Thread t2 = new Thread(c);t1.start();t2.start();}}class Cus implements Runnable{Bank b = new Bank();public void run() {for(int x =0;x<3;x++){b.add(100);}}}class Bank{private int sum=0;<span style="color:#ff0000;">public synchronized void  add(int n){//同步函数sum = sum+n;try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"sum"+sum);}</span>}
通过上面的实例就会出现一个问题,同步函数用的是哪一个锁呢?
3.1.1 锁中的this
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。
用下面的例子来说明:(购票的例子)
public class ThisLockDemo {public static void main(String[] args) {Sale s = new Sale();Thread t1 = new Thread(s);Thread t2 = new Thread(s);t1.start();try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}s.flag = false;t2.start();}}class Sale implements Runnable{private int ticket = 100;boolean flag = true;public void run() {if(flag){while(true){<span style="color:#ff0000;">synchronized(</span><strong>this</strong><span style="color:#ff0000;">){if(ticket>0){try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"...code:"+ticket--);}}</span>}}else{while(true){show();}}}<span style="color:#ff0000;">public synchronized void show() {//</span><strong>this</strong><span style="color:#ff0000;">if(ticket >0){try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"...show:"+ticket--);}}</span>}
通过该程序进行验证。使用两个线程来买票。一个线程在同步代码块中。一个线程在同步函数中。都在执行买票动作。所以同步函数的锁就是this

3.2 静态的同步函数:
如果同步函数被静态修饰后,使用的锁是什么呢?

通过验证,发现不在是this。因为静态方法中也不可以定义this。
静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。类名.class  该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class

下面是静态同步函数的实例(购票的例子)
public class StaticSaleDemo {/** * @param args */public static void main(String[] args) {Ticket1 t = new Ticket1();Thread t1 = new Thread(t);Thread t2 = new Thread(t);t1.start();try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}t.flag = false;t2.start();}}class Ticket1 implements Runnable{private static int ticket = 100;boolean flag = true;public void run(){if(flag){while(true){synchronized(<span style="color:#ff0000;">Ticket1.class</span>){if(ticket>0){try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"code"+ticket--);}}}}else{while(true)show();}}private static synchronized void show() {//<span style="color:#ff0000;">Ticket1.class</span>if(ticket>0){try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"code"+ticket--);}}}
3.3.死锁:同步中嵌套着同步:
用同上的例子来说明:
public class DeadLock {/** * @param args */public static void main(String[] args) {Ticket2 t = new Ticket2();Thread t1 = new Thread(t);Thread t2 = new Thread(t);t1.start();try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}t.flag = false;t2.start();}}class Ticket2 implements Runnable{private static int ticket = 100;boolean flag = true;static Object o = new Object();public void run(){if(flag){while(true){synchronized(o){//同步代码块中嵌套同步函数(同步中嵌套同步)这里的对象是Object类型,锁定之后调用show方法show();//show方法的锁的对象是Ticket2.class等着这个解锁}}}else{while(true)show();}}private static synchronized void show() {//show方法的锁的对象是Ticket2.classsynchronized(o){//同步函数中嵌套同步代码块(同步中嵌套同步)这里的对象是Object类型,等着解锁,于是都等着解锁最终形成了死锁if(ticket>0){try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"code"+ticket--);}}}}
五.线程间的通信:
5.1其实就是多个线程操作同一个资源,但是操作的动作不同
举例:比如现在又一个人的对象(姓名和性别),一个class是存入人对象,一个class是输出人对象。线程间通信和安全的方法就是线程同步,并且引用同一个对象
public class Comunnication {/** * @param args */public static void main(String[] args) {per p1 = new per();input i = new input(p1);output o = new output(p1);Thread t1 = new Thread(i);Thread t2 = new Thread(o);t1.start();t2.start();}}class per{String name;String sex;}class input implements Runnable{private per r;input(per p){this.r = p;}public void run() {int x = 0;while(true){synchronized(r){//两个类操作同一个对象rif(x==0){r.name="lili";r.sex = "女";}else{r.name = "zhangsan";r.sex = "男";}}x= (x+1)%2;}}}class output implements Runnable{private per r;output(per r){this.r = r;}public void run() {while(true){synchronized(r){//两个类操作同一个对象System.out.println(r.name+"..."+r.sex);}}}}
5.2 
问题一:wait(),notify(),notifyAll(),用来操作线程为什么定义在Object类中?
(1)这些方法存在同步中
(2)使用这些方法必须要标识所属的同步锁
(3)锁可以是任意对象,所以任意对象调用的方法一定定义在Object类中
问题二:
wait(),sleep()有什么区别
wait():释放资源,释放锁
sleep():释放资源,不释放锁

对上面的例子,实现存入一个人,然后就打印出一个人,那么就要用到等待和唤醒机制
例一:
public class Comunnication {/** * @param args */public static void main(String[] args) {per p1 = new per();/*input i = new input(p1);output o = new output(p1);Thread t1 = new Thread(i);Thread t2 = new Thread(o);t1.start();t2.start();*///代码优化:new Thread(new input(p1)).start();new Thread(new output(p1)).start();}}class per{String name;String sex;boolean flag = false;}class input implements Runnable{private per r;input(per p){this.r = p;}public void run() {int x = 0;while(true){synchronized(r){//两个类操作同一个对象rif(r.flag==false)try {r.wait();//如果是假的话,就让当前线程等待,执行其他线程} catch (InterruptedException e) {e.printStackTrace();}if(x==0){r.name="lili";r.sex = "女";}else{r.name = "zhangsan";r.sex = "男";}x= (x+1)%2;r.flag = false;r.notify();//唤醒等待的第一个线程}}}}class output implements Runnable{private per r;output(per r){this.r = r;}public void run() {while(true){synchronized(r){//两个类操作同一个对象if(r.flag==true){try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(r.name+"..."+r.sex);r.flag = true;r.notify();}}}}
例子二:多个生产者和消费者的问题
class CustomerAndProducer{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)//用if可能导致生产两个才消费一个!(if只能用于生产者1个,消费者一个的情况)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)try{wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);flag = false;this.notifyAll();//用while时候再用notify可能会使全部等待,无法继续,所以用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();}}}
(1)对于多个生产者和消费者,为什么要定义while判断标记
原因:让被唤醒的线程再一次判断标记
(2)为什么要定义notifyAll
因为需要唤醒对方的线程,因为只有用notify,容易出现唤醒本线程的情况。导致程序中的所有线程都等待。
5.3  jdk1.5唤起对方线程新特性:
之前用notifyAll唤醒的对方和我方的所有线程,但现在只想唤起对方的线程,所以JDK5.0升级版本(lock,unlock)
JDK1.5 中提供了多线程升级解决方案。
将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。
该对象可以Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。

Lock:替代了Synchronized
lock 
unlock
newCondition()

Condition:替代了Object wait notify notifyAll
await();
signal();
signalAll();
例子程序:
实现了生产和消费交替进行
import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;import com.sun.org.apache.bcel.internal.generic.NEW;public class CustomerAndProducer2 {/** * @param args */public static void main(String[] args) {Res r = new Res();Pro p = new Pro(r);Cus1 c = new Cus1(r);Thread t1 = new Thread(p);Thread t2 = new Thread(c);t1.start();t2.start();}}class Res{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){lock.lock();try {while(flag){try {condition_pro.await();} catch (InterruptedException e) {e.printStackTrace();}}this.name = name+"..."+count++;System.out.println(Thread.currentThread().getName()+"...customer"+this.name);flag = true;condition_con.signal();} finally{lock.unlock();}}public void out(){lock.lock();try {while(!flag){try {condition_con.await();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName()+"...producer"+this.name);flag = false;condition_pro.signal();} finally {lock.unlock();}}}class Cus1 implements Runnable{private Res r;Cus1(Res r){this.r = r;}public void run() {while(true){r.out();}}}class Pro implements Runnable{private Res r;Pro(Res r){this.r = r;}public void run() {while (true){r.set("+商品+");}}}

5.3停止线程:
  (1)定义循环结束标记
因为线程运行的代码一般都是循环,只要控制了循环即可
  (2)使用interrupt(中断)方法
该方法是结束线程的冻结状态,使线程回到运行状态中来
注意:stop方法已经过时不在使用
如何停止线程?
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法 interrupt();
举一个例子来说明:
public class StopThread {public static void main(String[] args) {Stop s = new Stop();Thread t1 = new Thread(s);Thread t2 = new Thread(s);t1.start();t2.start();int num = 0;while(true){if(num++==5){//s.ChangeFlag();t1.interrupt();t2.interrupt();break;}System.out.println(Thread.currentThread().getName()+"..."+num);}System.out.println("over");}}class Stop implements Runnable{private boolean flag = true;public synchronized void run() {while(true){try{wait();}catch (InterruptedException e){System.out.println(Thread.currentThread().getName()+"...Exception");flag = false;}System.out.println(Thread.currentThread().getName()+"...run");}}public void ChangeFlag(){flag = false;}}
上面的结果:
main...1
main...2
main...3
main...4
main...5
over
Thread-0...Exception
Thread-0...run
Thread-1...Exception
Thread-1...run
5.4 线程守护
setDaemon(true)
将线程标记为守护线程或用户线程。当正在运行的线程都是守护线程的时候,Java虚拟机退出,该方法必须在启动线程钱调用
比如上面的例子:
public class StopThread {/** * @param args */public static void main(String[] args) {Stop s = new Stop();Thread t1 = new Thread(s);Thread t2 = new Thread(s);t1.setDaemon(true);t2.setDaemon(true);t1.start();t2.start();int num = 0;while(true){if(num++==5){//s.ChangeFlag();//t1.interrupt();//t2.interrupt();break;}System.out.println(Thread.currentThread().getName()+"..."+num);}System.out.println("over");}}class Stop implements Runnable{private boolean flag = true;public synchronized void run() {while(true){try{wait();}catch (InterruptedException e){System.out.println(Thread.currentThread().getName()+"...Exception");flag = false;}System.out.println(Thread.currentThread().getName()+"...run");}}public void ChangeFlag(){flag = false;}}
上面代码的结果
main...1
main...2
main...3
main...4
main...5
over
5.5 join,yield,setPriority
join:
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。
yield:暂停当前正在执行的线程对象,并且执行其他线程
setPriority:设置优先级:所有线程默认的优先级是5;
MAX_PRIORITY:线程可以具有最高的优先级
MIN_PRIORITY:线程可以具有最低的优先级
NORM_PRIORITY:分配给线程默认的优先级

例如:
public class JoinThread {/** * @param args * @throws InterruptedException  */public static void main(String[] args) throws InterruptedException {JoinDemo jd = new JoinDemo();Thread t1 = new Thread(jd);Thread t2 = new Thread(jd);t1.join();//抢夺cpu的执行权,主线程等待t1执行完后,才能继续执行。t1.setPriority(Thread.MAX_PRIORITY);//设置优先级t2.start();//t1.join();//主线程碰到了t1是释放执行权,t1和t2交替执行,直到t1完成后主线程复活,不管t2是否结束。for(int x = 0;x<80;x++){System.out.println("main..."+x);}System.out.println("over");}}class JoinDemo implements Runnable{public void run() {for(int x = 0;x<10;x++){System.out.println(Thread.currentThread().getName()+"..."+x);Thread.yield();//暂停当前的线程执行其他的线程}}}
开发中线程的书写:
class ThreadTest {public static void main(String[] args) {new Thread(){public void run(){for(int x = 0;x<100;x++){System.out.println(Thread.currentThread().getName()+"..."+x);}}}.start();for(int x = 0;x<100;x++){System.out.println(Thread.currentThread().getName()+"..."+x);}Runnable r = new Runnable(){public void run(){for(int x = 0;x<100;x++){System.out.println(Thread.currentThread().getName()+"..."+x);}}};new Thread(r).start();//new Test1().start();}}/*class Test1 extends Thread{public void run(){for(int x = 0;x<100;x++){System.out.println(Thread.currentThread().getName()+"..."+x);}}}*/




































0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 孩子不愿意开口说话怎么办 孩子不爱开口说话怎么办 宝宝犟脾气不好怎么办 小孩说话不算话怎么办 孩子说话不算话怎么办 孩子故意不好好说话怎么办 小孩说话吐字不清楚怎么办 腿老是抽筋是怎么办 半夜睡觉脚抽筋怎么办 我不爱说话内向怎么办 小孩子吐字不清怎么办 宝宝前边头发少怎么办 宝宝咬嘴唇龅牙怎么办 小孩老是咬下唇怎么办 五月小孩掉下床怎么办 小孩说话夹舌头怎么办 小孩自闭不说话怎么办 孩子突然不说话怎么办 孩子说话语速慢怎么办 做磁共振不睡觉怎么办 宝宝吃饭不多怎么办 孩子吃饭爱说话怎么办 孩子吃饭喜欢说话怎么办 小孩子讲话嘴巴歪怎么办 小孩有自闭倾向怎么办 小孩笨反应慢怎么办 2岁不会说话怎么办 老公冷战不说话怎么办 小孩个子不高怎么办 额头发际线不齐怎么办 婴儿睡眠时间少怎么办 智商低不会说话怎么办 说话老是喷口水怎么办 数胎动宝宝不动怎么办 早教业绩不好怎么办 宝宝一岁还不会走路怎么办 两岁半宝宝开始叛逆怎么办 两岁半宝宝很叛逆怎么办 两岁半宝宝特别叛逆怎么办 小儿说不了话怎么办 二岁不会说话怎么办