java基础-多线程

来源:互联网 发布:中国广告服务贸易数据 编辑:程序博客网 时间:2024/05/22 17:22

1、多线程概述

进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立控制单元。线程在控制着进程的执行。且一个进程中至少有一个线程。
多线程:就是一个进程中存在多个线程,同时运行多部分代码。

JVM启动时会有一个进程java.exe。
该进程至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中,该线程称之为主线程。
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。

如何在自定义的代码中,自定义一个线程呢?
-->通过api的查找,java已经提供了对线程这类事物的描述--Thread类。

2、创建线程的第一种方式:继承Thread类。
步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。用于存储线程要运行的代码。
3,创建Thread的子类对象,即创建线程。
4,调用该线程的start方法。该方法有两个作用:启动线程,调用run方法。
例:创建一个线程打印数字0-60,同时主线程也打印数字0-60。
<span style="font-family:Microsoft YaHei;font-size:14px;">class Demo extends Thread//定义类继承Thread。{public void run()//复写Thread类中的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();//创建Thread的子类对象,即创建好一个线程。//面试会问以下两个语句的区别d.start();//调用该线程的start方法,启动线程,并调用run方法。//d.run();//仅仅是对象调用方法。而线程创建了,但并没启动。for (int x=0; x<60; x++)System.out.println("main run............."+x);}}</span>


运行结果1:


运行结果2:


发现运行结果每一次都不同。
因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一时刻,只能有一个程序在运行。(多核除外)cpu在做着快速切换,以达到看上去是同时运行的效果。我们可以形象地把多线程的运行形容为在互相抢夺cpu的执行权。
这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长时间,cpu说的算。
为什么要覆盖run方法呢?
-->Thread类用于描述线程。该类定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。
例:创建两个线程,和主线程交替运行。
<span style="font-family:Microsoft YaHei;font-size:14px;">class Test extends Thread{private String name;Test(String name){this.name = name;}public void run(){for (int x=0; x<60; x++)System.out.println(name+"demo run------"+x);}}class  ThreadTest{public static void main(String[] args) {Test t1 = new Test("one");Test t2 = new Test("two");    t1.start();    t2.start();for (int x=0; x<60; x++)System.out.println("main run............."+x);}}</span>
3、线程运行状态


4、获取线程对象及名称
设置线程名称:setName或者构造函数。
获取当前线程对象:static Thread currentThread()
获取线程名称:getName()
例:
<span style="font-family:Microsoft YaHei;font-size:14px;">class Test extends Thread{//private String name;Test(String name){//this.name = name;super(name);//设置线程名称}public void run(){for (int x=0; x<60; x++)System.out.println(Thread.currentThread().getName()+"---demo run------"+x);//获取当前线程对象及线程名称。}} class  ThreadTest1{public static void main(String[] args) {Test t1 = new Test("one");Test t2 = new Test("two");    t1.start();    t2.start();for (int x=0; x<60; x++)System.out.println("main run............."+x);}}</span>
5、创建线程的第二种方式:实现Runnable接口。
步骤:
1,定义类实现Runnable接口。
2,复写Runnable接口中的run方法。将线程要运行的代码放在该run方法中。
3,创建Runnable接口的子类对象。
4,通过Thread类创建线程对象
5,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
6,调用Thread类的start方法启动线程并调用Runnable接口子类的run方法。
例:简单的买票程序实现多个窗口同时卖票。
<span style="font-family:Microsoft YaHei;font-size:14px;">/**需求:简单的买票程序实现多个窗口同时卖票。*/class Ticket implements Runnable//extends Thread //定义类实现Runnable接口。{private int tick = 100;public void run()//复写Runnable接口中的run方法。{while(true){if(tick>0){System.out.println(Thread.currentThread().getName()+"---sale:"+tick--);}}}} class  TicketDemo{public static void main(String[] args) {Ticket t = new Ticket();//创建Runnable接口子类对象。 Thread t1 = new Thread(t);//通过Thread类创建线程对象。将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。Thread t2 = new Thread(t);Thread t3 = new Thread(t);Thread t4 = new Thread(t);t1.start();//调用Thread类的start方法t2.start();t3.start();t4.start(); /*Ticket t1 = new Ticket();Ticket t2 = new Ticket();Ticket t3 = new Ticket();Ticket t4 = new Ticket(); t1.start();t2.start();t3.start();t4.start();*/}}</span>
6、实现方式和继承方式的区别
继承Thread:线程代码存放在Thread子类run方法中。
实现Runnable:线程代码存放在接口的子类run方法中。
实现的好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。

7、多线程的安全问题
问题原因:当多条语句在操作同一个线程的共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:对多条操作共享数据的语句,让一个线程都执行完。在此线程执行过程中,其他线程不可以参与执行。
例:
<span style="font-family:Microsoft YaHei;font-size:14px;">class Ticket implements Runnable//extends Thread {private int tick = 100;public void run(){while(true){if(tick>0){try{Thread.sleep(10);}catch(Exception e){}//模拟出现安全问题System.out.println(Thread.currentThread().getName()+"---sale:"+tick--);}}}} class  Issue{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();}}</span>
运行结果:


分析:打印出0,-1,-2等错票。


8、同步代码块
格式:
synchronized(对象)
{
    需要被同步的代码
}
对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使获取cpu的执行权,也进不去。
同步的前提:
1,必须要有两个或两个以上的线程。
2,必须是多个线程使用同一个锁。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
例:
<span style="font-family:Microsoft YaHei;font-size:14px;">class Ticket implements Runnable{private int tick = 100;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  Issue{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();}}</span>
运行结果:


9、同步函数
同步函数:用synchronized修饰的函数。
同步函数的锁是this。
静态同步函数使用的锁是该函数所在类的字节码文件对象。类名.class
如何找安全问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
例:
<span style="font-family:Microsoft YaHei;font-size:14px;">/**需求:银行有一个金库。有两个储户分别存300元,每次存100,存3次。*/class Bank{private int sum;public synchronized void add(int n)//同步函数{sum = sum + n;try{Thread.sleep(10);}catch(Exception e){}//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);}}}class BankDemo {public static void main(String[] args) {Cus c = new Cus();Thread t1 = new Thread(c);Thread t2 = new Thread(c);t1.start();t2.start();}}</span>
例:多线程单例设计模式-懒汉式
分析:懒汉式可以延时实例加载,多线程访问时会出现安全问题,可以加同步解决,但是有些低效,每一个线程都要判断锁 ,用双重判断的方式可以解决低效的问题,同步时使用的锁是该类的字节码文件。
<span style="font-family:Microsoft YaHei;font-size:14px;">/*单例设计模式饿汉式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)//双重判断//--->A s = new Single();}}return s;}}</span>
10、死锁
死锁:同步中嵌套同步。  要会写死锁程序。
例:
<span style="font-family:Microsoft YaHei;font-size:14px;">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  DeadLockDemo{public static void main(String[] args) {Thread t1 = new Thread(new Test(true));Thread t2 = new Thread(new Test(false));t1.start();t2.start();}}</span>
运行结果:


11、线程间通信-等待唤醒机制
线程间通信:其实就是多个线程在操作同一个资源,但是操作的动作不同。
等待唤醒机制:if(flag)--wait()--notify()               一个生产者对应一个消费者 
                        while(flag)--wait()--notifyAll()    多个生产者对应多个消费者
使用等待唤醒机制时,要有一个标记控制线程等待还是执行代码。
注意:
1,wait()--notify()     wait()--notifyAll()  必须使用在同步中。
      因为要对持有监视器的线程操作,而只有同步才有锁。
2,等待和唤醒必须是同一个锁。且必须要标识出wait--notify所操作线程持有的锁。锁.wait()
例:if(flag)--wait()--notify()  输入一组数据,输出显示这组数据。   一个生产者对应一个消费者
<span style="font-family:Microsoft YaHei;font-size:14px;">class Res{String name;String sex;boolean flag = false;} class Input implements Runnable{private Res r;Input(Res r){this.r = r;}public void run(){int x = 0;while (true){synchronized(r){if(r.flag)//标记等待还是执行try{r.wait();}catch(Exception e){} //等待  锁.wait()if(x==0){r.name = "mike";r.sex = "man";}else{r.name = "花千骨";r.sex = "女女女女";}r.flag = true;r.notify();//唤醒 锁.notify()}x = (x+1)%2;}}} class Output implements Runnable{private Res r;Output(Res r){this.r = r;}public void run(){while(true){synchronized(r){if(!r.flag)try{r.wait();}catch(Exception e){}//等待  锁.wait()System.out.println(r.name+"....."+r.sex);r.flag = false;r.notify();//唤醒 锁.notify()}}}}class  InputOutputDemo{public static void main(String[] args) {Res r = new Res(); Input in = new Input(r);Output out = new Output(r); Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start();}}</span>
运行结果:


代码优化:
<span style="font-family:Microsoft YaHei;font-size:14px;">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  InputOutputDemoyouhua{public static void main(String[] args) {Res r = new Res();     new Thread(new Input(r)).start();new Thread(new Output(r)).start();}}</span>
例: while(flag)--wait()--notifyAll()    多个生产者对应多个消费者
<span style="font-family:Microsoft YaHei;font-size:14px;">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)//if(flag)  本方对象没判断标记导致生产两个,却只消费一个。while让被唤醒的线程再一次判断标记。try{this.wait();}catch(Exception e){}this.name = name+"---"+count++;System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);flag = true;this.notifyAll();//this.notify();容易出现只唤醒本方对象的情况,导致所有对象全部等待。}public synchronized void out(){while(!flag)//if(!flag)try{this .wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"...消费者........"+this.name);flag = false;this.notifyALL();//this.notify();}} class Producer implements Runnable{private Resource res;Producer(Resource res){this.res = res;}public void run(){int x = 0;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>
运行结果:


12、JDK1.5新特性  lock接口-condition对象
将同步synchronized替换成显示Lock操作
将Object中的wait-notify-notifyAll替换成condition对象的await-signal-signalAll
该对象可以通过Lock锁进行获取。
例:
<span style="font-family:Microsoft YaHei;font-size:14px;">import java.util.concurrent.locks.*;class  ProducerConsumerDemoJDKNEW{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();//锁Lock对象,一个锁可以建立多个condition对象private Condition condition_pro = lock.newCondition();//condition对象private Condition condition_con = lock.newCondition();//condition对象 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 synchronized 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(){int x = 0;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){}}}}</span>
13、停止线程
1,定义循环结束标记
        因为多线程运行代码通常是循环结构,只要控制住循环,就可以让run方法结束,也就是线程结束。
例:
<span style="font-family:Microsoft YaHei;font-size:14px;">class StopThread implements Runnable{private boolean flag = true;//定义循环结束标记public void run(){while(flag){System.out.println(Thread.currentThread().getName()+".......run");}}public void changeFlag(){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 = 0;while(true){if(num++  ==  60){st.changeFlag();//改变循环结束标记break;}System.out.println(Thread.currentThread().getName()+"......."+num);}}}</span>
运行结果:


2,使用Thread类的interrupt(中断)方法
        该方法是结束线程的冻结状态,使线程恢复到运行状态中来,这样就可以操作标记让线程结束。
stop方法已经过时。
例:
<span style="font-family:Microsoft YaHei;font-size:14px;">class StopThread implements Runnable{private boolean flag = true;//定义循环结束标记public synchronized void run(){while(flag){try{wait();// 冻结状态}catch (InterruptedException e){System.out.println(Thread.currentThread().getName()+".......InterruptedException");flag = false;//操作标记,让线程结束}System.out.println(Thread.currentThread().getName()+".......run");}}public void changeFlag(){flag = false;}} class  StopThreadDemoInterrupt{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 = 0;while(true){if(num++  ==  60){//st.changeFlag();t1.interrupt();t2.interrupt();break;}System.out.println(Thread.currentThread().getName()+"......."+num);}System.out.println("over"); }}</span>
运行结果:


14、守护线程
setDaemon(true)
将该线程标记为守护线程,当正在运行的线程都是守护线程时,java虚拟机退出
该方法必须在启动线程前调用
如:   t1.setDaemon(true);
t2.setDaemon(true);
t1.start();
t2.start();
15、join方法
join:当A线程执行到了B线程的.join()方法时,A就会等待,等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。
16、优先级
Thread.currentThread().toString();返回该线程的字符串表示形式。包括线程名称、优先级、线程组。
t1.setpriority(Thread.MAX_PRIORITY);更改线程优先级。
Thread.yield();暂停当前正在执行的线程对象。并执行其他线程。




0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 淘宝地址中包含了违禁词怎么办 微信支付失败但是钱扣了怎么办 支付宝向别人收款交易关闭了怎么办 从淘宝充的晋江币充值异常怎么办 接手转让店铺会员要求退卡怎么办 转转买手机卖家拒绝退款怎么办 淘宝买的东西电话号码留错了怎么办 平板电脑没电关机没保存文件怎么办 恢复出厂设置需要谷歌账号怎么办 华为手机云端里照片删除了怎么办 客户退货卖家一直没收到货怎么办 在淘宝买到假货投诉不管用怎么办 差评不接电话不回旺旺不要钱怎么办 饿了么同行恶意差评怎么办 苹果手机更新后淘宝用不了怎么办 淘宝网快递丢件了买家怎么办 评价后忘了截图五星好评怎么办 在淘宝被骗了好评返现怎么办 苹果4s微信版本过低怎么办 微信版本太低无法登录怎么办 苹果4微信版本低登录不了怎么办 安卓手机微信版本低登录不了怎么办 苹果微信版本低登录不了怎么办 安装包与当前版本不兼容怎么办 游戏与苹果手机系统不兼容怎么办 闲鱼买家申请退款不退货怎么办 在淘宝买的战地1登录不了怎么办 支付宝转账到注销的账号怎么办 支付宝用户支付密码被锁定怎么办 淘宝提现需要的手机宝令怎么办 手机换号码了支付宝账号怎么办 卖家收到货迟迟不给退款怎么办 闲鱼东西已发出买家申请退款怎么办 如果东西在路上买家要退款怎么办 卖家收到退的货后不退款怎么办 扣48分淘宝被限制登录怎么办 淘宝网申请退款卖家不理怎么办 高考生如果把登录密码忘记了怎么办 普高考生登录密码忘记了怎么办 淘宝买的流量卡然后网卡怎么办? 忘记了qq号绑定了手机怎么办