黑马程序员 自学日记(五)线程

来源:互联网 发布:软件开发费用包括 编辑:程序博客网 时间:2024/04/30 10:02

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IO开发S</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------

 

线程

进程是一个正在执行中的程序

             每一个进程执行都有一个执行顺序该顺序是一个执行路径或者叫一个控制单元。

线程:是进程中的一个独立的控制单元

             线程在控制着进程的执行

一个进程中至少有一个进程

java VM启动的时候一个进程java.exe

该进程中至少一个线程负责java程序的执行

创建线程的第一种方法,继承Thread

步骤

1 定义类继承Thread

2 复写Thread类中的run方法

3 调用线程的start方法

为什么要重写run方法呢?

Thread类用于描述线程,

该类中值定义了一个功能用于存储该线程要运行的代码该功能就是run方法

也就是说Thread类中的run方法是用于存储线程要运行的代码

class Demo extends Thread

{

       //Demo(String name)

       //{

       //     super(name);

       //}

       public void run()

              {

                     for (int x = 0;x<60 ;x++ )

                     {

                            System.out.println(getName()+x);//getName方法获取线程名

                     }

              }

}

class ThreadDemo

{

       public static void main(String[] args)

       {

              Demo d =new Demo();

              d.start();

              for (int x = 0;x<60 ;x++ )

              {

                     System.out.println("hello world---"+x);

              }

       }

}

线程创建的第二张方法实现Runnable接口

步骤:

1定义实现Runable接口

2 覆盖Runnable接口中的run方法

3通过Thread类创建对象

4Runnable接口的子类对象作为实际参数传递给Thread类的构造函数

5调用Thread类的start方法开启线程并调用Runnable接口子类的run方法

实现方式与继承方式有什么区别呢?

实现方式避免了单继承的局限性

定义线程方式建议使用实现方式

继承Thread类线程代码存放在Thread子类run方法

实现Runnable线程代码存放在接口子类run

class Ticket implements Runnable//extends Thread

{

       private  int tick =100;

       public void run()

       {

              while(true)

              {

                     if (tick>0)

                     {

                            System.out.println(Thread.currentThread().getName()+"---"+tick--);

                     }

              }

       }

}

class ThreadText

{

       public static void main(String[] args)

       {

              Ticket t = new Ticket();

              Thread t1 = new Thread(t);//创建一个线程对象,对象内实现一个

              Thread t2 = new Thread(t);//Runnable内的run方法

              Thread t3 = new Thread(t);

              Thread t4 = new Thread(t);

              t1.start();

              t2.start();

              t3.start();

              t4.start();

              Ticket t2 = new Ticket();

              Ticket t3 = new Ticket();

              Ticket t4 = new Ticket();*/

       }

}

通过分析,发现打印出了0 -1 -2等错票

多线程的运行出现了安全问题。

 

问题的原因

       当多条语句在操作同一个线程共享数据时,一个线程多条语句

       只执行一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误

 

解决办法:

       对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行

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

就是同步代码块

synchronized(对象){}

       同步是解决线程的安全隐患

       同步的前提:

              1必须要有两个或者两个以上线程

              2必须多个线程使用同一个锁并保证同步里只有一个线程

这里对象如同一把锁持有锁的线程可以在同步代码块中执行

而没有持有锁的线程即使获取了CPU的执行权也无法进去执行线程

class Ticket implements Runnable   发现打印出了0 -1 -2等错票

{

       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()+"---"+tick--);

                     }

              }

       }

}

class Ticket implements Runnable//extends Thread

{

       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()+"---"+tick--);

                            }

                     }

              }

       }

}

class ThreadText

{

       public static void main(String[] args)

       {

              Ticket t = new Ticket();

              Thread t1 = new Thread(t);//创建一个线程对象,对象内实现一个

              Thread t2 = new Thread(t);//Runnable内的run方法

              Thread t3 = new Thread(t);

              Thread t4 = new Thread(t);

              t1.start();

              t2.start();

              t3.start();

              t4.start();

       }

}

需求:

银行有一个金库

有两个储户分别300元,分别存三次每次存100

目的:该程序是否有安全问题如果有该如何解决

 

如何找问题

1 明确哪些代码是多线程运行代码

2明确共享数据

3 明确多线程运行代码中哪些语句是操作共享数据的

 

同步有两种表现形式

第一是同步代码块 synchrnoized(){}

 

一种是将synchronized作为修饰符来修饰一个函数

同步代码块用的是对象锁,那么同步函数的锁呢?

 

函数需要被对象调用,那么函数都有一个所属对象引用,就是this

所以同步函数使用的锁就是this

class Bank

{

       private int sum;

       //Object obj =new Object();

       public synchronized void add(int n)

       {

              //synchronized(obj)

              sum = sum+n;

              try

              {

                     Thread.sleep(2);

              }

              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();

       }

}

单例模式懒汉式(单例模式延时加载示例)

       懒汉式的特点是用于实例的延时加载,但是在多线程延时加载的过程有安全问题,可以加同步解决。同步的方式有同步函数,同步代码块,同步说使用的锁是该类所属的字节码对象。

Class Single

{    

       private static Single s = null;

       private Single(){}

       public static void mian(String[] args)

       {

              if(s==null)

              synchronized(Single.class)

              {

       If(s==null)

       s=new Single();

}

return s;

}

}

线程之间的通信

/*

线程间通讯

其实就多个线程操作同一个资源,但是操作的动作不同。

wait();notify();notifyAll();这三个方法都必须只能使用在同步中,因为要对持有监视器(锁)

的线程进行操作。所以才要使用在同步中,因为只有同步才会有锁。

 

为什么这些操作线程的方法定义在Object类中呢?

因为这些方法在操作同步线程时,都必须要标示他们所操作线程的锁,只有同一个锁上的

线程才可以被同一个线程锁上的notify唤醒。也就是说等待和唤醒必须是同一个监视器,而

锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

 

*/

class Res

{

       String name;

       String sex;

       boolean flag =false;

}

 

class Input implements Runnable

{

       private Res r;//对象r的引用

       Input(Res r)

       {

              this.r= r;

       }

       public void run()

       {

              int x =0;

 

                     while (true)

                     {

                            synchronized(r)//同步两个线程公共操作的代码 锁是两个线程共同引用的对象r

                            {

                                   if (r.flag)

                                          try{r.wait();}//Object类中的wait方法有异常必须要try

                                          catch (Exception e){}

                                   if (x==0)

                                   {

                                          r.name="mike";

                                          r.sex="man";

                                   }

                                   else

                                   {

                                          r.name="lili";

                                          r.sex = "nv";

                                   }

                                   x = (x+1)%2;

                                   r.flag = true;

                                   r.notify();

                            }

                     }

       }

}

class Output implements Runnable

{

       private Res r;

       Output(Res r)

       {

              this.r =r;

       }

       public  void run()

       {

              synchronized(r)

              {

                     while (true)

                     {

                            if (!r.flag)

                                   try{r.wait();}catch(Exception e) {}

                            System.out.println(r.name+" ------ "+r.sex);

                            r.flag = false;

                            r.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();

       }

}

/*

JDK1.5以后的多线程唤醒机制 Object中的wait notify替换了Condition对象。

*/

import java.util.concurrent.locks.*;

class Resource

{

       private String name;

       private int count =1;

       private boolean flag =false;

       private Lock lock = new ReentrantLock();//创建一个lock锁的子类对象

       private Condition condition_pro =  lock.newCondition();//根据锁创建锁的实例对象

       private Condition condition_con = lock.newCondition();

 

       public void set(String name)throws InterruptedException

       {

              lock.lock();

              while (flag)

                     try

                     {

                            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//实现一个Runnable接口的run方法

{

       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()

       {

              try

              {

                     res.out();

              }

              catch (InterruptedException e)

              {

              }

       }

}

class TextDemo

{

       public static void main(String[] args)

       {

              Resource r = new Resource();

 

              new Thread ( new producer(r)).start();

              new Thread ( new Consumer(r)).start();

              new Thread ( new producer(r)).start();

              new  Thread ( new Consumer(r)).start();

 

       }

}

 

 

---------------------- <a href="http://edu.csdn.net"target="blank">ASP.Net+Android+IOS开发</a>、<a href="http://edu.csdn.net"target="blank">.Net培训</a>、期待与您交流! ----------------------