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

来源:互联网 发布:java telnet 编辑:程序博客网 时间:2024/05/17 07:30
------- android培训、java培训、期待与您交流! ----------




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

自定义线程的方法:
1:继承Thread类,复写run方法(定义自己需要的内容),然后创建Thread子类对象,调用start方法。
package Date423;public class TreadDemo  {public static void main(String[]args){Demo d = new Demo();//创建线程d.start();//开启线程}}class Demo extends Thread//继承Thread{public void run()//覆盖run方法{System.out.println("demorun");}}
这是有俩个线程,主线程跟d线程,  当他们都要进行运行,cpu会分别对每个线程进行切换式的运算。(单核)
 线程的默认名称,从Thread-0开始。
static Thread currentTread():获取当前线程对象
通过getName方法获取线程名称
通过setName(或者构造函数)设置线程名称
局部的变量在每一个线程中都有独立的一份。
package Date423;public class TreadDemo  {public static void main(String[] args) {Test t1 = new Test("one");//创建一个线程,传入一个名字Test t2 = new Test("two");t1.start();//运行一个线程t2.start();}}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++)//线程交替运行{//Thread.currentThread() == thisSystem.out.println(this.getName()+"=-----运行");}}}

售票例子:

package Date423;public class TreadDemo1 {public static void main(String[] args) {Ticket t1 = new Ticket();Ticket t2 = new Ticket();Ticket t3 = new Ticket();Ticket t4 = new Ticket();t1.start();t2.start();t3.start();t4.start();}}class Ticket extends Thread{private static int tick =100;public void run(){while(tick>0){System.out.println(Thread.currentThread().getName()+"sale:"+tick--);}}}
已经运行的程序不能被再次启动。

2,
实现Runnable接口:
a,定义类实现Runnable接口,
        b,覆盖Runnable接口中的run方法(存放线程运行的方法)
c,通过Thread类建立线程对象
d,将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数(自定义的run方法属于Runnable接口的子类对象,让线程去运行指定对象的run方法。)
e,调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

public class ThreadDemo2 {public static void main(String[] args) {Ticket1 t = new Ticket1();Thread t1 = new Thread(t);//创建线程对象并将调用run方法对象传入Thread t2 = new Thread(t);Thread t3 = new Thread(t);Thread t4 = new Thread(t);t1.start();t2.start();t3.start();t4.start();}}class Ticket1 implements Runnable{private static int tick =100;public void run(){while(tick>0){System.out.println(Thread.currentThread().getName()+"sale:"+tick--);}}}


俩种创建线程对象的区别:
1.继承在java中只能继承一次,而java可以多实现。
2,继承Thread后线程方法存放在Thread子类run方法中
3,实现Runnable,线程代码存放在接口的子类的run方法中(而线程启动都是用Thread,所以需要将Runnable的子类提供给Thread类)

多线程运行的问题:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,另外一个线程参与进来,导致共享数据错误。

解决办法:
对多条操作共享数据的语句,只能允许一个线程执行完,其他线程才能进入执行。

java对于多线程的安全问题提供了解决方式

同步代码块:
synchronized(对象)对象如同锁,持有锁的线程能执行代码(火车上的卫生间)
{
需要被同步的代码,
}

同步的前提:
1,必须有多个线程
2,必须是同一个对象锁

好处:解决了多线程的安全问题
弊端:效率低。

class Ticket1 implements Runnable{private static int tick =100;public  void run(){synchronized(new Object())//将代码进行同步,//同步代码一般是多线程操作共享数组的代码块。{while(tick>0){System.out.println(Thread.currentThread().getName()+"sale:"+tick--);}}}}

案例:银行存钱。  银行有一个金库,有俩个储户分别存300,一次存100

package Date423;public class TreadDemo3 {public static void main(String[] args) {Cus c = new Cus();Thread t1 =new Thread(c,"zhangsan");Thread t2 =new Thread(c,"lisi");Thread t3=new Thread(c,"wangwu");t1.start();t2.start();t3.start();}}class Cus implements Runnable//实现Runnable{private Bank b =new Bank();public void run() {// TODO Auto-generated method stubfor(int x =0;x<3;x++){b.add(100);}}}class Bank{Object obj = new Object();private int sum;public synchronized void add(int n)//让函数具备同步性{//synchronized(obj)//同步代码块//{sum = sum+n;try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(sum);//}}}

synchronized放在函数上时,就是同步函数。
同步必须有锁,函数是被对象调用的,那么函数都有一个所属对象引用,同步函数的锁就是this。
如果同步函数被静态修饰后  使用的锁是类的字节码文件对象  类名.class。
package Date423;public class ThreadDemo4 {public static void main(String[] args) {Ticket2 t = new Ticket2();Thread t1 = new Thread(t);//创建线程对象并将调用run方法对象传入Thread t2 = new Thread(t);t1.start();t2.start();}}class Ticket2 implements Runnable{private static int tick =100;boolean flag = true;public  void run(){if(flag){while(true){synchronized(this)//说明锁为this。{if(tick>0){try{Thread.sleep(10);}catch(Exception e){};System.out.println(Thread.currentThread().getName()+"......."+tick--);}}}}elsewhile(tick>0){show();}}//}//}public synchronized void show()//如果改为静态方法,则同步的方法show的锁就不是this了//因为静态在类一加载的时候先于对象,它的锁是类.class字节码文件对象.{if(tick>0){try{Thread.sleep(10);}catch(Exception e){};System.out.println(Thread.currentThread().getName()+"sale:"+tick--);}}}

单例设计模式

饿汉式
class Single
{
private static final Single s = new Single();

private(){};

public static Single getInstance()
{
return s;
}
}

懒汉式
<span style="font-size:18px;">class Single{private static  Single s = null;private(){}public static  Single getInstance()</span>
<span style="font-size:18px; font-family: Arial, Helvetica, sans-serif;">{</span>
<span style="font-size:18px;"><span style="white-space:pre"></span>if(s==null)//双重判断提高效率<span style="white-space:pre"></span>{synchronized(Single.class){  //加锁  锁用getInstance所属的字节码对象<span style="white-space:pre"></span>if(s==null)s = new Single();<span style="white-space:pre"></span>}return s;<span style="white-space:pre"></span>}<span style="white-space:pre"></span>}}</span>



死锁:同步中嵌套同步,线程持有的锁是其他线程需要的锁,其他线程也持有自己需要的锁时会发生死锁。




线程间的通信:



等待机制:wait()等待,将线程存入线程池中;notify()唤醒先存入线程池中的线程;notifyAll()将线程池中的数据全部唤醒。 因为这三个方法需要监视器,所以必须运用在同步中,而线程中可能存在不同的锁,所以需要标明使用这些方法的对象(锁)  对象.wait();因为锁是任意对象,所以wait notify notifyAll  需要定义在Object中。同一个锁wait与 notufy  botifyAll 之间的 功能才能实现

<pre name="code" class="java">public class ThreadDemo5 {public static void main(String[] args) {Res r = new Res();new Thread(new Input(r)).start();new Thread(new Output(r)).start();/** Input in = new Input(r);Output out = new Output(r);Thread t1 = new Thread(in);Thread t2 = new Thread(out);t1.start();t2.start(); */}}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 (InterruptedException e) {//// TODO Auto-generated catch block//e.printStackTrace();//}//System.out.println(r.name+"...."+r.sex);r.out();//r.flag =false;//r.notify();//将r锁中的wait线程唤醒////}}}}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 (InterruptedException e) {//// TODO Auto-generated catch block//e.printStackTrace();//}if(x==0){r.set("mike","man");}else{r.set("丽丽","女女女女女女");}x=(x+1)%2;//让男女间隔存入//r.flag=true;//r.notify();////}}}}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();}}


生产者消费者案例:
package Date423;//生产者消费者案例public class ThreadDemo6 {public static void main(String[] args) {Resource res =new Resource();//创建厂房Producer pr = new Producer(res);//创建生产者对象Consumer cs = new Consumer(res);//创建消费者对象Thread t1 = new Thread(pr);//开启俩条产线  俩条消费线Thread t2 = new Thread(pr);Thread t3 = new Thread(cs);Thread t4 = new Thread(cs);t1.start();//运行t2.start();t3.start();t4.start();}}class Resource{private String name;//定义一个私有的字符串private int count =1;//定义一个私有的标记private boolean flag = false;//定义一个boolean标记public synchronized void set(String name)//同步函数,传入生产的东西名称{while(flag)//如果flag就等    for循环因为线程被叫醒以后不再判断标记而导致数据错乱,  while循环会导致线程全部等待。//使用notifyAll  可以全部唤醒  并且循环判断标记try{this.wait();}catch(Exception e){}this.name = name+"----"+count++;//否则字符串就改成设置的name+标记System.out.println(Thread.currentThread().getName()+"....生产者"+this.name);//输出线程名加次数flag = true;//将标记改为truenotifyAll();//省略this锁,唤醒其他同锁的线程}public synchronized void out()//同步函数{while(!flag)//如果真,等待try{wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"....消费者......."+this.name);//输出线程名称跟产品消费的产品flag = false;//更改标记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();}}}





LOCK(jdk1.5)替代了
synchronized
方法:lock()加锁,unlock()解锁。
Condition:await()等待  signal()唤醒  signalAll()唤醒全部。
lock可以对应多个Condition对象。(singnal方法就可以唤醒不同的线程。)



package Date423;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class TreadDemo7 {public static void main(String[] args) {Resource1 res =new Resource1();//创建厂房Producer1 pr = new Producer1(res);//创建生产者对象Consumer1 cs = new Consumer1(res);//创建消费者对象Thread t1 = new Thread(pr);//开启俩条产线  俩条消费线Thread t2 = new Thread(pr);Thread t3 = new Thread(cs);Thread t4 = new Thread(cs);t1.start();//运行t2.start();t3.start();t4.start();}}class Resource1{private String name;//定义一个私有的字符串private int count =1;//定义一个私有的标记private boolean flag = false;//定义一个boolean标记private Lock lock= new ReentrantLock();private Condition con = lock.newCondition();private Condition con1 = lock.newCondition();public  void set(String name)//同步函数,传入生产的东西名称{lock.lock();try{while(flag)con.await();this.name = name+"----"+count++;//否则字符串就改成设置的name+标记System.out.println(Thread.currentThread().getName()+"....生产者"+this.name);//输出线程名加次数flag = true;//将标记改为true//省略this锁,唤醒其他同锁的线程con1.signal();//con.signalAll();}catch(Exception  e){}finally{lock.unlock();}}public synchronized void out()//同步函数{lock.lock();try{while(!flag)//如果真,等待con1.await();System.out.println(Thread.currentThread().getName()+"....消费者......."+this.name);//输出线程名称跟产品消费的产品flag = false;//更改标记 //con.signalAll();//唤醒同锁线程con.signal();}catch(Exception e){}finally{lock.unlock();}}}class Producer1 implements Runnable{//创建同步类,用作生产产品,private Resource1 res;Producer1(Resource1 res){this.res=res;}public void run(){//覆盖方法while(true){try{res.set("+商品+");}catch(Exception e){}}}}class Consumer1 implements Runnable{//用于消费private Resource1 res;Consumer1(Resource1 res){this.res=res;}public void run(){while(true){try{res.out();}catch(Exception e){}}}}



停止线程:只有一种方法,就是run方法结束。开启多线程运行,通常都用循环结构,只要控制住循环,就可以让线程结束。
当线程处于了同步情况下,线程被冻结后,就不会读取到标记,线程就不会结束
用interrupt()将冻结状态的线程恢复到运行状态,这样就可以操作标记让线程结束。


Thread类中的其他方法

setDaemon(boolean flag); 后台线程,当flag为true时,  前台线程运行结束以后,后台线程会自动结束。
join();  主线程在运行中,如果有线程同过join方法开启线程,则这个线程将会抢走cpu执行权,当join线程结束时 ,主线程才恢复到运行状态。
toString();Tread.currentThread().toString();线程组
setPriority();  设置优先级1-10级,默认都是5。1,5,10比较明显。 10-Tread.MAX_PRIORITY, 1,5  。
yield();暂停当前正在执行的线程。(临时停止)。稍微减缓线程的运行。
























0 0
原创粉丝点击