多线程实现方式
1)、实现Runnable接口,并实现run()方法
以下是主要步骤:
1、自定义类并实现Runnable接口,并实现run()方法。
2、创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象。
3、调用Thread的start()方法
class MyThread implements Runnale{
public void run(){
//线程执行代码块
}
}
public class Test{
public static void main(String []args){
MyThread thread=new Thread();
Thread t=new Thread(thread);
t.start();
}
}
2)、继承Thread类,重写run方法。
Thread本质上也是实现了Runnable接口的一个实例,它代表了一个线程实例,并且启动线程的唯一方法是通过Thread类的start方法。start方法是一个native(本地)
方法,他将启动一个新线程,并执行run方法(这是一个空方法);这种方式通过自定义类直接继承extends Thread,并重写run方法,就可以启动新线程并执行自己定义的run方法
需要注意的是:当start()方法调用之后并不会直接立即执行多线程代码,而是使得该线程变为可运行状态,什么时候运行多线程代码是由操作系统决定的
class MyThread extends Thread{
public void run(){
}
}
public class Test{
public static void main(String [] args){
MyThread thread=new Thread();
thread.start();
}
}
3)、实现Callable接口,重写call()方法
Callable随想实际上属于Executor框架中的功能类,Callable接口与Runnable接口类似,但是提供了比Runnable更强大的功能,主要表现如下:
- Callable可以在任务结束后提供一个返回值,Runnable无法提供这个功能
- Callable中的call方法可以抛出异常,而Runnable的run方法不能抛出异常
- 运行Callable可以拿到一个future对象,future对象表示异步计算的结果,它提供了检查计算是否完成的方法。由于线程属于异步计算模型,所以无法从其他线程中得到方法的返回值,在这种情况下,就可以使用future来监视
目标线程调用call方法的情况,当调用future的get方法以获取结果时,当前线程就会阻塞,直到call()方法结束后返回结果。
public class CallableAndFuture{
public static class CallableTest implements Callable<String>{
public String call() throws Exception {
return "Hello world";
}
}
public static void main(String []args){
Executor threadpool=Executor.newSingleThreadExecutor();
Future<String> future=thread.submit(new CallableTest());
try{
Sytem.out.println(future.get());//等待线程结束,并获取返回结果
}catch(){
}
}
}
线程提供同步机制
主要有三种实现同步机制的方式
1)synchronized 关键字
在java中,每一个对象都有一个对象锁与之相关联,该锁表明对象在任何时候只允许被一个线程所拥有,当一个线程调用对象的一段synchronized代码时,首先需要获得这个锁,然后去执行相应的代码,执行结束后,释放锁。
synchronized关键字主要有两种使用方式(synchronized方法和synchronied代码块),此关键字还可以运用于静态方法、类或某一个实例,但是对程序的效率影响很大。
- synchronized方法:
public synchronized void mutiThreadAccess();
只要把多个线程访问的资源操作放到mutiThreadAccess方法中,就能够保证这个方法在同一刻只能被一个线程来访问,从而保证了多个线程访问的安全性。然而,当一个方法的方法体规模非常大的时候会影响程序执行效率。所以提供了synchronized代码块。
- synchronized代码块
可以把任意的代码段申明为synchronized,也可以指定上锁的对象,有非常高的灵活性,
synchronized (syncObject){
//访问同步代码
}
2)wait与notify
当使用synchronized来修饰某一个共享资源时候,如果线程A1在执行synchronized代码,另外一个线程A2也要同时执行同一对象的同一synchronized代码时,线程A2将要
等到线程A1执行完毕后才能执行。在这种情况下,可以使用wait方法和notify方法。
在synchronized代码执行期间,线程可以调用对象的wait方法,释放对象锁,进入等待状态,并且可以调用notify方法或notifyAll方法通知正在等待的其他线程,notify方法
仅唤醒一个线程(等待队列中的第一个线程),并允许它去获得锁,而notifyAll方法唤醒所有等待这个对象的线程,并允许他们去获得锁(并不是让所有唤醒的线程都获得锁,而是让他们去竞争);
3)Lock
jdk5 新增了Lock接口以及他的实现类ReentrantLock(重入锁),lock也可以用来实现多线程同步,具体而言,他提供了如下的一些方法来实现多线程的同步:
1)lock().以阻塞的方式来获取锁,也就是说,如果获取到了锁,则立即返回,如果其他线程持有锁,当前线程等待,直到获取锁后返回。
2)tryLock() 以非阻塞方式获取锁。只是尝试性的去获取锁,如果获取到锁,则立即返回true,获取不到则返回false,
3)tryLock(long timeout,TimeUnit unit) 如果获取了锁,立即返回true,否则,会等待参数给定时间单元,在等待的过程中,如果获取了锁,就返回true,如果等待时间超过,则返回false
4)lockInterruptibly().如果获取了锁,则立即返回,如果没有获取锁,则当前线程处于休眠状态,直到获得锁,或者当前线程被其他线程中断(会收到InterruptedException异常),他与lock方法最大的区别在于
:如果lock方法获取不到锁,则会一直处于阻塞状态,且会忽略interrupt方法。
final Lock lock=new ReentrantLock();
lock.lock();
Thread t1=new Thread(new Runnable(){
public void run{
try{
lock.lockInterruptibly();
//lock.lock();
}cathch(InterruptedException e){
System.out.println(“interrupted”);
}
}});
t1.strart();
t1.interrupt();
Thread.sleep(1);
如果吧lock.lockInterruptibly()替换为lock.lock(),编译器将会提示lock.lock catch代码块无效,因为lock.lock()不会抛出异常,由此可见,lock方法会忽略interrupt()引发的异常。