Java多线程

Java多线程

Java多线程需要掌握两种实现方式。一种是继承Thread类,另一种是实现Runable接口。


先来看看两种实现方式的定义:

Thread类:

public class Thread extends Object implements Runnable
可以看到,Thread类继承了Runable接口。

@API A thread is a thread of execution in a program. The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.

线程是执行在程序中,Java的虚拟机允许有多个线程同时进行。


Runable接口:

public interface Runnable

@API The Runnable interface should be implemented by any class whose instances are intended to be executed by a thread. The class must define a method of no arguments called run.

需要用到线程的类都应该实现Runable接口。

@API This interface is designed to provide a common protocol for objects that wish to execute code while they are active. For example,Runnable is implemented by class Thread. Being active simply means that a thread has been started and has not yet been stopped.


实现多线程的基本方法:

Demo1:继承Thread类

<pre name="code" class="java">public class threadingTest1 {

 public class ThreadDemo1 {
     public static void main(String[] args){
         Demo d = new Demo();
         d.start();
         for(int i=0;i<60;i++){
             System.out.println(Thread.currentThread().getName()+i);
         }

     }
 }
 class Demo extends Thread{
     public void run(){
         for(int i=0;i<60;i++){
             System.out.println(Thread.currentThread().getName()+i);
         }
     }
 }

 

Demo2:实现Runable接口

<pre name="code" class="java">public class threadingTest1 {

	public static void main(String[] args) {
		Demo2 d = new Demo2();
		Thread t = new Thread(d);
		t.start();
		for (int i = 0; i < 60; x++) {
			System.out.println(Thread.currentThread().getName() + i);
		}
	}
}

class Demo2 implements Runnable {
	public void run() {
		for (int i = 0; i < 60; x++) {
			System.out.println(Thread.currentThread().getName() + i);
		}
	}
}

 

常用的Thread的构造方法:

Thread()
//Allocates a new Thread object.
Thread(Runnable target)
//Allocates a new Thread object.
Thread(Runnable target, String name)
//Allocates a new Thread object.
Thread(String name)
//Allocates a new Thread object.
Thread(ThreadGroup group, Runnable target)
//Allocates a new Thread object.
Thread(ThreadGroup group, Runnable target, String name)
//Allocates a new Thread object so that it has target as its run object, has the specified name as its name, and belongs to the thread group referred to by group.
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
//Allocates a new Thread object so that it has target as its run object, has the specified name as its name, and belongs to the thread group referred to by group, and has the specified stack size.
Thread(ThreadGroup group, String name)
//Allocates a new Thread object.


然后看一下线程的生命周期:

开始(等待)、运行、挂起、停止

查看一下Thread类的方法:

  • // 开始线程
  • publicvoid start( );
  • publicvoid run( );
  • // 挂起和唤醒线程
  • publicvoid resume( );     // 不建议使用
  • publicvoid suspend( );    // 不建议使用
  • publicstaticvoid sleep(long millis);
  • publicstaticvoid sleep(long millis, int nanos);
  • // 终止线程
  • publicvoid stop( );       // 不建议使用
  • publicvoid interrupt( );
  • // 得到线程状态
  • publicboolean isAlive( );
  • publicboolean isInterrupted( );
  • publicstaticboolean interrupted( );
  • // join方法
  • publicvoid join( ) throws InterruptedException;

在使用sleep方法时有两点需要注意:

1. sleep方法有两个重载形式,其中一个重载形式不仅可以设毫秒,而且还可以设纳秒(1,000,000纳秒等于1毫秒)。但大多数操作系统平台上的Java虚拟机都无法精确到纳秒,因此,如果对sleep设置了纳秒,Java虚拟机将取最接近这个值的毫秒。

2. 在使用sleep方法时必须使用throws或try{…}catch{…}。因为run方法无法使用throws,所以只能使用try{…}catch{…}。当在线程休眠的过程中,使用interrupt方法中断线程时sleep会抛出一个InterruptedException异常。sleep方法的定义如下:

  1. publicstaticvoid sleep(long millis) throws InterruptedException
  2. publicstaticvoid sleep(long millis, int nanos) throws InterruptedException

三种方法可以使终止(杀死!)线程。

1.  使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。

2.  使用stop方法强行终止线程(这个方法不推荐使用,因为stop和suspend、resume一样,也可能发生不可预料的结果)。

3.  使用interrupt方法中断线程。


线程的安全问题:

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

解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不执行。


举个例子:售票处有四个窗口,一共有400张tickets,然后卖掉!

把四个窗口独立为四个线程,tickets为四个进程都需要访问的数据。

首先使用同步代码块的方法

<pre name="code" class="java">public class threadingTest1 {

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

class Ticket implements Runnable {
	private int ticket = 400;

	public void run() {
		while (true) {
			synchronized (new Object()) {//使用synchronized(new Object)同步代码块
				try {
 					Thread.sleep(1);
 				} catch (InterruptedException e) {
					 // TODO Auto-generated catch block
					 e.printStackTrace();
				 }
 				if (ticket <= 0)
 					break;
				 System.out.println(Thread.currentThread().getName() + "---卖出"  + ticket--);
 			}//同步代码块
		}
	 }
}

 

使用同步方法

public class ThreadDemo3 {
    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();
    }
}
class Ticket implements Runnable{
    private int ticket = 4000;
    public synchronized void  saleTicket(){
        if(ticket>0)
            System.out.println(Thread.currentThread().getName()+"卖出了"+ticket--);

    }
    public void run(){
        while(true){
            saleTicket();
        }
    }
}


 
那么使用同步代码块和同步方法的区别是什么呢? 

我见过比较好的解释还有通过反编译分析字节码的方法

方法级同步:- 实现方法:在要标志为同步的方法前加上synchronized关键字。- 实现原理:当调用对象的同步方法时,线程取得对象锁或监视器;如果另一个线程试图执行任何同步方法时,他就会发现他被锁住了,进入挂起状态,直到对象监视器上的锁被释放时为止。当锁住方法的线程从方法中返回时,只有一个排队等候的线程可以访问对象。代码块级同步:- 临界区:需要进行互斥的代码段,而非整个方法。- 实现方法:用synchronized来指定某个对象,此对象的锁被用来对花括号内的代码进行同步控制。- 实现原理:在进入同步代码前,必须得到object对象的锁,如果其他线程已经得到这个锁,那么就得等到锁被释放后才能进入临界区。- 锁的作用域:只在代码块运行的时间内。

又来看一下synchronized这个同步的关键字作用

Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。然而,当一个线程访问object的一个加锁代码块时,另一个线程仍然可以访问该object中的非加锁代码块。

由于Thread类继承于Object类的,因此Object类中一些方法也是thread中常用的:

wait() 和 notify();

wait()带参数的,和不带参数的

voidwait()
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
voidwait(long timeout)
Causes the current thread to wait until either another thread invokes the notify() method or the notifyAll() method for this object, or a specified amount of time has elapsed.
voidwait(long timeout, int nanos)
Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object, or some other thread interrupts the current thread, or a certain amount of real time has elapsed.
作用是使当前的线程等待,直到另一进程使用notify()或notifyAll()方法来唤醒

那相对地,notify()方法

voidnotify()
Wakes up a single thread that is waiting on this object's monitor.
voidnotifyAll()
Wakes up all threads that are waiting on this object's monitor.
唤醒对此此Objec监视中的t等待中的进程

最后,一个线程通讯的例子:

 

public class ThreadDemo4 {
    private static boolean flags =false;
    public static void main(String[] args){
        class Goods{
            private String name;
            private int num;
            public synchronized void produce(String name){
                while(flags)
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                this.name =name+"编号:"+num++;
                System.out.println(Thread.currentThread().getName()+"生产了...."+this.name);
                flags =true;
                notifyAll();
            }
            public synchronized void consume(){
                while(!flags)
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                System.out.println(Thread.currentThread().getName()+"消费了******"+name);
                flags =false;
                notifyAll();
            }

        }
        final Goods g =new Goods();
        new Thread(new Runnable(){
            public void run(){
                while(true){
                    g.produce("商品");
                }
            }
        },"生产者一号").start();
        new Thread(new Runnable(){
            public void run(){
                while(true){
                    g.produce("商品");
                }
            }
        },"生产者二号").start();
        new Thread(new Runnable(){
            public void run(){
                while(true){
                    g.consume();
                }
            }
        },"消费者一号").start();
        new Thread(new Runnable(){
            public void run(){
                while(true){
                    g.consume();
                }
            }
        },"消费者二号").start();
    }
}
/*
消费者二号消费了******商品编号:48049
生产者一号生产了....商品编号:48050
消费者一号消费了******商品编号:48050
生产者一号生产了....商品编号:48051
消费者二号消费了******商品编号:48051
生产者二号生产了....商品编号:48052
消费者二号消费了******商品编号:48052
生产者一号生产了....商品编号:48053
消费者一号消费了******商品编号:48053
生产者一号生产了....商品编号:48054
消费者二号消费了******商品编号:48054
生产者二号生产了....商品编号:48055
消费者二号消费了******商品编号:48055
*/


部分内容参考自码农网




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值