您的位置:首页 > 职场人生

黑马程序员--多线程

2015-05-06 17:17 302 查看

------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------



多线程:

线程和进程

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

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

 

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

                   线程在控制着进程的执行。

 

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

 

 

 

JAVA VM 启动的时候会有一个进程java.exe。

 

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

而且这个线程运行的代码存在于main方法中。

该线程称之为主线程。

 

扩展:其实更细节说明jvm,jvm启动不止衣蛾线程,还有负责垃圾回收机制的线程、

 

创建线程的方式:

 

创建线程的第一种方式:继承Thread类。

步骤:

1,定义类继承Thread;

2,复写Thread类中的run 方法。

         目的:将自定义代码存储在run方法。让线程运行。

 

3,调用线程的方法,

         该方法有两个左右:启动线程,调用run方法。

 

 

为什么要覆盖run方法呢?

 

Thread类用于描述线程。

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

 

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

示例:

package com.itheima;
 
public
class
ThreadDemo {
    public
static void
main(String[]
args) {
       ////创建好一个线程。开启线程并执行该线程的run方法。
       new Demo().start();
       new Demo().start();
//     new Demo().run();仅仅是对象调用方法。而线程创建了。并没有运行。
    }
}
class Demo
extends
Thread{
 
    @Override
    public
void
run() {
       // TODO Auto-generatedmethod stub
       for (int
i = 0; i < 60;
i++) {
           System.out.println("i="+i);
       }
    }
}

 

创建线程的第二种方式:实现Runable接口

 

步骤

1,定义类实现Runnable接口

2,覆盖Runnable接口中的run方法。

         将线程要运行的代码存放在run方法中

 

3,通过Thread类建立线程对象。

4,将Runnable接口的子类对象作为事件参数传递给Thread类的构造函数。

         为什么要将Runnable接口的子类对象传递给Thread的构造函数。

         因为,自定义的run方法所属的对象是Runnable接口的子类对象。

         所以要让线程去执行指定对象的run方法。就必须明确该run方法所属的对象。

 

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

 

示例:

package com.itheima;
 
public
class
RunnableDemo {
    public
static void
main(String[]
args) {
       //创建了一个线程.
       new Thread(new Demo2()).start();
       new Thread(new Demo2()).start();
    }
}
class Demo2
implements
Runnable{
 
    @Override
    public
void
run() {
       // TODO Auto-generatedmethod stub
       for(int
x = 0 ; x<60;x++){
           System.out.println("x="+x);
       }
    }
}

 

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

 

实现方法的好处:避免了单继承的局限性。

在定义线程时,建议使用实现方式。

 

两种方式的区别:

继承Thread:线程代码存放Thread子类run方法中。

实现Runnable,线程代码存在接口子类的run方法。

 

线程运行状态:(自己画得,有点丑,见谅)

 


线程的安全问题

 

导致线程安全问题出现的原因:

多个线程访问出现延迟

线程的随机性。

线程安全在理想状态下不容易出现,但是一旦出现对元件的影响是巨大的。

解决方法:加入同步代码块:

格式:

         synchronized(对象){

         需要同步的代码;

}

 

同步的特点:

同步的前提:

同步需要两个或者两个以上的线程。

多个线程使用的是同一个锁。

未满足这两个条件,不能称为同步

同步的弊端

         当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。

 

 

示例:

未使用synchroniezd代码:(加入sleep语句加以测试。)

package com.itheima;
 
public
class
TicketDemo {
    public
static void
main(String[]
args) {
       Tick t= new Tick();
 
       new
Thread(t).start();
       new
Thread(t).start();
       new
Thread(t).start();
    }
}
class Tick
implements
Runnable{
 
    private
int
tick = 100;
 
 
    @Override
    public
void
run() {
       // TODO Auto-generatedmethod stub
       while(true){
           if(tick>0){
              try {
                  Thread.sleep(30);
              } catch (InterruptedException
e) {
                  // TODO Auto-generatedcatch block
                  e.printStackTrace();
              }
              System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
           }
       }
    }
}发现在输出结果中有-1,-2等,这是由于当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

 

解决办法:

    对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。(java中提供了专业的解决方法,就是同步代码块)

使用同步代码块后:
 
package com.itheima;
 
public
class
TicketDemo2 {
    public
static void
main(String[]
args) {
       Tick2 t= new Tick2();
 
       new Thread(t).start();
       new Thread(t).start();
       new Thread(t).start();
    }
}
class Tick2
implements
Runnable{
 
    private
int
tick = 100;
    Object obj = new Object();
 
    @Override
    public
void
run() {
       // TODO Auto-generatedmethod stub
       while(true){
           synchronized (this) {
              if(tick>0){
                  try {
                     Thread.sleep(10);
                  } catch (InterruptedException
e) {
                     // TODO Auto-generatedcatch block
                     e.printStackTrace();
                  }
                  System.out.println(Thread.currentThread().getName()+"sale:"+tick--);
              }
           }
       }
    }
}
 

*同步函数:

格式:在函数上加上sychroniezed修饰符即可。

         publicstatic sychronized void xxx(Xxx x){}

那么同步函数用的是哪个锁呢?

         函数需要被对象调用,那么函数都有一个所属对象引用,就是this,所以,用的是this。同步函数被this修饰后使用的锁是什么呢?通过验证,不再是this,因为今天函数没有this,静态进内存,内存中没有本类对象,但是一定有该类对应的字节码文件à类名.class,该对象的类型是Class

 

死锁

死锁。

同步中嵌套同步。

 

示例:

package com.itheima;
 
public
class
DieLockDemo {
    public
static void
main(String[]
args) {
        new Thread(new Ticket(true)).start();
        new Thread(new Ticket(false)).start();
    }
}
class Ticket
implements
Runnable{
    private
boolean
flag;
 
    public Ticket(boolean
flag) {
       super();
       this.flag =
flag;
    }
 
    @Override
    public
void
run() {
       // TODO Auto-generatedmethod stub
       if(flag){
           synchronized (MyLock.locka) {
              System.out.println("iflock--a");
              synchronized (MyLock.lockb) {
                  System.out.println("if lock--b");
              }
           }
       }else{
           synchronized (MyLock.lockb) {
              System.out.println("elselock--b");
              synchronized (MyLock.locka) {
                  System.out.println("else lock--a");
              }
           }
       }
    }
}
 
class MyLock{
    static Object
locka = new Object();
    static Object
lockb = new Object();
}

 

线程间的通讯:

其实就是多个线程操作同一资源。

但是操作不同。

等待唤醒机制(wait() notify())

wait();notify();notifyAll();

都使用在同步中,因为要持有监视器(锁)的线程操作。所以要使用在同步中,因为同步才有锁。

为什么这些操作线程时要定义在Object类中呢?

因为这些方法在操作同步中的线程时都必须要标识他们所操作线程只有锁,只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒。

也就是说,等待和唤醒必须是同一个锁。

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

 

 

 

package com.itheima;
 
public
class
InputOutputDemo {
    public
static void
main(String[]
args) {
       Res r= new Res();
       new Thread(new Input(r)).start();
      
       new Thread(new
Output(r)).start();
    }
   
}
class Res{
    String name;
    String sex;
    boolean
flag = false;
   
    public
synchronized void
set(String
name,String sex){
       if(flag){
           try {
              this.wait();
           } catch (InterruptedException
e) {
              // TODO Auto-generatedcatch block
              e.printStackTrace();
           }
       }
       this.name=name;
       this.sex=sex;
      
       flag = true;
       this.notify();
    }
    public
synchronized void
out(){
       if(!flag){
           try {
              this.wait();
           } catch (InterruptedException
e) {
              // TODO Auto-generatedcatch block
              e.printStackTrace();
           }
       }
       System.out.println(name+".."+sex);
      
       flag=true;
       this.notify();
    }
}
class Input
implements
Runnable{
    private Res
r;
    int
x = 0;
   
    public Input(Res
r) {
       super();
       this.r =
r;
    }
 
    @Override
    public
void
run() {
       // TODO Auto-generatedmethod stub
       while(true){
           if(x==0){
              r.set("mike",
"man");
           }else{
              r.set("丽丽",
"女");
           }
           x=(x+1)%2;
       }
    }
}
class
Outputimplements Runnable{
    private Res
r;
   
    public Output(Res
r) {
       super();
       this.r =
r;
    }
 
    @Override
    public
void
run() {
       // TODO Auto-generatedmethod stub
       while(true){
           r.out();
       }
    }
   
}

 

同步1.5升级(Lock)

Lock对象的建立:

Lock lock = new ReentranLock();

Lock.lock();

Lock.unlock();

Object替代:Condition 

await(); single();  singleAll();

Condition contidion = lock.newCondition();

该对象可以和Lock进行获取。

 

 

示例:

package com.itheima;
 
import java.util.concurrent.locks.ReentrantLock;
 
public
class
LockDemo {
    public
static void
main(String[]
args) {
       LockTest lt = new LockTest();
       new Thread(lt).start();
       new Thread(lt).start();
       new Thread(lt).start();
    }
}
class LockTest
implements Runnable{
    private
int
x= 50;
    ReentrantLock lock =
new
ReentrantLock();
    @Override
    public
void
run() {
       // TODO Auto-generatedmethod stub
       while(true){
           lock.lock();
           if(x>0){
              try {
                  Thread.sleep(10);
              } catch (InterruptedException
e) {
                  // TODO Auto-generatedcatch block
                  e.printStackTrace();
              }
              System.out.println(Thread.currentThread().getName()+":"+x--);
           }
           lock.unlock();
       }
    }
}

 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: