黑马程序员--java--多线程

来源:互联网 发布:移动网络怎么样 编辑:程序博客网 时间:2024/06/05 08:11
------<a href="http://www.itheima.com" target="blank">Java培训、Android培训、iOS培训、.Net培训</a>、期待与您交流! -------

多线程

1.1 多线程的概念
进程:正在进行中的程序。
线程:进程中一个负责执行的控制单元。

解析:
1.一个进程中可以有多个线程,称之为多线程。
2.一个进程中至少有一个线程,称为主线程
3.开启多个线程是为了同时运行多部分代码,每个线程都有自己运行的内容,这个内容称为线程需要执行的任务。

多线程的好处:解决了多部分代码同时运行的问题。
多线程的弊端:线程太多会导致效率的降低。
程序执行特点:其实, 多个应用程序同时执行都是CPU在做着快速的切换完成的。 这个切换是随机的。 CPU的切换是需要花
费时间的, 从而导致了效率的降低。
1.2 线程创建于执行
创建的方式之一:继承Thread类
1.定义一个继承Thread类。
2.覆盖Thread类中的run方法。
3.直接创建Thread的子类对象创建线程。
4.调用start方法开启线程并调用线程的run方法。

thread中的几种常见方法:
1.Thread.currentThread():获取当前现成的对象。
2.t.getName();获取t线程对象的名称。格式:Thread-编号(从0开始)

示例:
class Demo extends Thread{String name ;Demo(String name){this.name = name;}public void run(){for(int x=0;x<10;x++){System.out.println(name+"... x="+x+"... ThreadName="+Thread.currentThread().getName());}} }class ThreadDemo{public static void main(String[] args){Demo d1 = new Demo("旺财");Demo d2 = new Demo("小黄");d1.start();d2.start();for(int x=0;x<10;x++){System.out.println("x="+x+"... ThreadName="+Thread.currentThread().getName());}}}

运行结果:
发现三个输出语句无序的打印出来。

创建的方式之二:实现Runnable接口
1.定义一个类实现Runnable接口。
2.覆盖接口中的run方法,将线程将要执行的代码放入其中。
3.通过Thread类创建对象,并把实现了Runnable的子类对象作为参数传给Thread类的构造函数。
4.调用Thread中的start方法,开启线程。

实现Runnable的好处:
1.将线程的任务从线程子类中分离出来,进行了单独的封装,安装面向对象的思想将任务封装成了对象。
2.避免了java单继承的局限性。
3.实现Runnable方法更为常见。

示例:
class Demo implements Runnable{public void run(){show();}public void show(){for(int x=0;x<10;x++){System.out.println("x="+x+"... ThreadName="+Thread.currentThread().getName());}}}class ThreadDemo{public static void main(String[] args){Demo d1 = new Demo();Thread t1 = new Thread(d1);Thread t2 = new Thread(d1);t1.start();//可简写为new Thread(d1).start();t2.start();}}

运行结果:两个线程无序的把0-9依次打印出来。
1.3 线程安全性问题。
举例说明问题:
需求:模拟4个窗口卖票的问题。
代码:
class Ticket implements Runnable{private int num = 100;public void run(){while(true){if(num>0){System.out.println(Thread.currentThread().getName()+"...sale...."+num--);} }}}class TicketDemo{public static void main(String[] args){Ticket t = new Ticket();new Thread(t).start();new Thread(t).start();new Thread(t).start();new Thread(t).start();}}

运行结果:
发现运行出现两个99和出现0,-1的情况。

分析:
因为cpu执行多线程时,是在多线程之间快速切换的,当线程-0执行到num=1但为对其自减就停止了,此时线程-1进来了num没有变化,输出后num自减num=0,回到线程-0,输出的就是num=0了。

线程安全问题的产生:
1.多个线程同时操作共享数据时。
2.操作的共享数据的代码有多条。
1.4 线程安全问题的解决方案
思路:将多个线程执行的代码封装在一个空间内,一次只让一个线程执行其中的代码。
方法:使用synchronized(同步)
格式:
synchronized(对象){
需要被同步的代码,即多线程执行的代码;
}

同步的好处:解决了多线程的安全性问题。
同步的弊端:当线程相当多时,同一时间只有一个线程在执行,降低了运行的效率。
同步的前提:必须有多个线程并使用同一个锁。

卖票修改后代码:
class Ticket implements Runnable{private int num = 100;Object obj  = new Object;//同步需要锁,锁为一个对象或者一个class文件,这里用Object对象。public void run(){while(true){synchronized(obj){if(num>0){System.out.println(Thread.currentThread().getName()+"...sale...."+num--);}} }}}

利用同步带代码块解决安全性问题:

需求:储户,两个,每个都到银行存钱,每次存入100,共存三次。

class Bank{private int sum;public void add(int num){synchronizd(this){sum = sum + num;System.out.println("sum = "+sum);}}}class Cus implements Runnable{private Bank b = new Bank();public void run(){for(int x = 1;x<=3;x++){d.add(100);}}}class BankDemo{public static void main(String[] args){Cus c = new Cus();new Thread(c).strat();new Thread(c).strat();}}

运行结果:
sum = 100
sum = 200
sum = 300
sum = 400
sum = 500
sum = 600


synchronized可作为修饰符放在函数中,称为同步函数。
格式:
public synchronized void add(int num){
同步代码;
}

同步代码和同步函数的区别:同步代码的锁为自定义的,而同步函数的锁为固定的this。建议使用同步代码块。

静态的同步函数使用的锁是干函数所属字节码文件对象,可以用 对象.getClass() 方法获取,也可以用 类.class() 表示,还可通过 Class.forName(类名) 获取。
1.5 死锁示例
一般出现在同步嵌套中,即同步中有同步,并且锁用的不同。

示例
class DieLock implement Runnable{Object obja = new Object();Object objb = new Object();boolean flag = true;public void run(){if(flag){synchroinzed(obja){System.out.println("hellow obja");synchroinzed(objb){System.out.println("hellow objb");}}}else{synchroinzed(objb){System.out.println("hellow objb");synchroinzed(obja){System.out.println("hellow obja");}}}}class Demo{public static void main(String[] args){DieLock d = new DieLock();new Thread(d).strat();new Thread(d).strat();}}

死锁运行后出现,光标停在一个位置不动。
1.6 线程间的通讯
当多个线程对同一资源进行操作,而操作的方法却不同时,就需要多线程通讯。

等待/唤醒机制涉及的方法:
1.wait():让线程处于冻结状态,被wait()的线程会被存储到线程池中。
2.notify():唤醒线程池中的线程(按被冻结的顺序)。
3.notifyAll():唤醒线程池中所有的线程。
4.这些方法都被定义在object中。

注意点:
1.这些方法必须定义在同步中,因为这些方法是用于操作线程状态的方法。
2.必须要明确到底操作的是哪个锁上的线程。
3.wait和sleep的区别
  1)wait可以指定时间也可以不指定时间,而sleep必须执行时间。
  2)在同步中,对cup执行权和锁的处理
     wait:释放执行权,释放锁。
    sleep:释放执行权,不释放锁。
4.两种方法都涉及到 InterruptedException 需要处理

输入/输出 姓名/性别 例:
class Resource{private String name;private String sex;boolean flag = false;public synchronized viod set(String name ,String age){if(flag){try{this.wait();}catch(InterruptedException e){e.printStackTrace();}}this.name = name ;this.sex = sex;flag = true;this.notify();}public synchronized void out(){if(!flag){try{this.wait();}catch(InterruptedException e){e.printStackTrace();}}System.out.println("name = "+name+"sex = "+sex);flag = false;this.notify();} }class Input implements Runnable{Resource r ;Input(Resource r){this.r = r;}public void run(){int x = 0while(true){if(x==0)r.set("张华","男");if(x==1)r.set("丽丽","女");x=(x+1)%2;}}}class Output implement Runnable{Resource r;Out(Resource r){this.r = r;}public void run(){while(true){r.out();}}}class ResourceDemo{public static void main(String[] args){Resource r = new Resource;new Thread(new Input(r)).strat();new Thread(new OutPut(r)).strat();}}

运行结果:
name = 张华sex =男
name = 丽丽sex =女
无序交替出现。


注意:当对资源的同一方法有多个线程在操作,判断用while,而且唤醒用notifyAll()。
1.7 JDK1.5特性(Lock)
Lock接口:出现代替同步代码块或者同步函数,将同步的隐式操作变成了显示操作。同时更为灵活,可以一个锁上加上多个监视器。

格式:
1.获得锁对象
  Lock lock = new ReentrantLock();
  lock.lock();
2.获得监视器(可获得多个监视器)
  Condition con_p = lock.newCondition();
  Condition con_c = lock.newCondition();
3.等待(有抛出异常)
  con_p.await();
4.唤醒
  con_p.signal();
5.释放锁(在finally语句中为一定要执行语句)
  lock.unlock();

示例:
多个生产者、消费者

class Resource{String name ;int count = 1;boolean flag = false;Lock lock = new ReentrantLock();Condition con_p = lock.newCondition();Condition con_c = lock.newCondition();public void set(String name){lock.lock();try{while(flag){try{con_p.await();}catch(InterruptedException e){e.printStackTrace();}}this.name = name + count;count++System.out.println(Thread.currentThread().getName()+"..生产.."+this.name);flag = true;con_c.signal();}finally{lock.unlock();}}public void out(){lock.lock();try{while(!flag){try{con_c.await();}catch(InterruptedException e){e.printStackTrace();}}System.out.println(Thread.currentThread().getName()+"..消费.."+this.name);flag = false;con_p.signal();}finally{lock.unlock();}}}class Producer implements Runnable{Resource r;Producer(Resource r){this.r = r;}public void run(){while(true){r.set("烤鸭");}}}class Consumer implements Runnable{Resource r;Producer(Resource r){this.r = r;}public void run(){while(true){r.out();}}}class ResourceDemo{public static void main(String[] args){Resource r = new Resource;new Thread(new Producer(r)).strat();new Thread(new Producer(r)).strat();new Thread(new Consumer(r)).strat();new Thread(new Consumer(r)).strat();}}

运行结果:
生产消费同一产品依次输出。
1.8 线程中其他方法:
1.停止线程:stop方法已经过时,怎么控制线程的结束,线程任务中都会有循环结构,只要控制循环就能结束任务。


2.t.interrupt():强制将处于冻结状态的t线程唤醒,但出现了InterruptedException异常需要处理。


3.t.setDeamon(true):守护线程,随着主线程的结束而结束。


4.t.join():抢夺执行权,等t线程运行完后在执行其他线程内容。


5.t.toString:返回线程的字符串表示形式:线程名称 优先级 线程组


6.t.setPriority(5):设置t线程的优先级(0-9),默认为5


7.t.yeild():停止t线程,执行其他线程。
0 0
原创粉丝点击