使用线程(一)

共三种使用线程的方法:

实现runnable接口

实现Callable接口

继承thread类

实现Runnable与Callable接口的类只能当作一个可以在线程中执行的任务,不是真正意义上的线程

实现Runnable接口因为比较熟悉,暂时不谈

实现Callable接口

与Runnable相比,Callable可以有返回值,返回值通过FutureTask进行封装

public classMyCallable implents Callable<Integer>{

	public Integer call(){
				return 123;
	}

}

public static void main(String[] args) throws ExecutionException ,InterruptedException{
	MyCallable mc =bew MyCallable();
	FutureTask<Integer> ft=new FutureTask<>(mc);
	Thread thread=new Thread(ft);
	thread.start();
}

利用Futuretask来接收返回值

继承Thread类

同样也是需要实现run()方法,因为Thread类也实现了Runable接口

当调用start()方法启动一个线程时,虚拟机会将该线程放入就绪队列中等待被调度,当一个线程被调度时就会执行该线程的run()方法

比较

还是实现接口好一点,

(1)Java不支持多继承

(2)类可能只要求可执行,继承Thread开销过大

基础机制

Executor

管理多个异步任务的执行(这里的异步是指多个任务的执行互不干扰,不需要进行同步操作)

主要有三种Executor

CachedThreadPoll:一个任务创建一个线程

FixedThreadPool:所有任务只能使用固定大小的线程

SinglethreadExecutor:相当于大小为1的FiexedthreadPool

public static void main(String[] args){
	ExecutorService executorService=Exectors.newCacheThreadPool();
	for(int i=0;i<5;i++){
		executorService.execute(new myRunnable());
	}
	executorService.shutdown();
}

Daemon

守护线程时程序运行时在后台提供服务的线程,不属于程序中不可或缺的部分

当所有非守护线程结束时,程序也就终止,同时会杀死所有的守护线程

main()属于非守护线程

在线程启动之前使用setDaemon()方法可以将一个线程设置为守护线程

public static void main(String[] args){
	Thread thread=new Thread(new MyRunnable());
	thread.setDaemon(true);
}

sleep

Thread.sleep方法会休眠当前正在执行的线程

可能会抛出InterruptedException,因为异常不能跨域线程传播回main()中,因此必须在本地进行处理

yield

调用此静态方法,声明了当前线程已经完成了生命周期中的最重要的部分,可以切换给其他线程来执行

public void run(){
	Thread.yield();
}

中断

一个线程执行完毕之后会自动结束,如果在运行过程中发生异常也会提前结束

InterruptedException

通过调用一个线程的interrupt()来中断该线程,如果该线程处于阻塞,限制等待或者无限期等待状态,那么就会抛出InterruptedException,从而把提前结束该线程。但是不能中断I/O阻塞和synchronizd锁阻塞

interrupted

如果一个线程的run()方法执行一个无限循环,并且没有执行sleep()等会抛出InterruptedException’的操作,那么调用线程的interrupted方法就无法使用线程提前结束

但是调用Interrupt方法会设置线程的中断标记,此时调用interrupted方法会返回true。因此可以在循环体中使用interrupted方法来判断线程是否处于中断状态

public class interruptExample{
			private staic class MyThread2 extends Thread{
					@Override
					public void run(){
								while(!interrupted()){
								}
					}
}

public static void main() throws InterruptedException{
	Thread thread2=new MyThread2();
	thread.start();
	thread.interrupt();
}

Executor的中断操作

调用Executor的shutdown方法会等待线程都执行完毕后再关闭,但是如果调用的是shutdownNoew()方法,则相当于调用线程的interrupt方法

如果只想中断Executer中的一个线程,可以通过使用submit()方法来提交一个线程,它会返回一个Future<?>对象,通过调用该对象的cancel(true)方法就可以中断线程

Future<?> future=executorService.submit(()->{

});
future.cancel(true);

互斥同步

java提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是JVM实现的synchronized,第二个是JDK实现的是ReentrantLock

synchronized

同步一个代码块

public void func(){
	synchronized(this){
	}
}

只作作用域同一个对象,如果调用两个对象以上的同步代码块,就不会进行同步

同步一个方法

public synchronzied void func(){
}

同步一个类

public void func(){
	synchronized(SynchronizedExample.class){
	
	}
}

作用与整个类,也就是说两个线程调用同一个类的不同对象上的这种同步语句,也会进行同步

同步一个静态方法

public synchronized static void fun(){
}

ReentrantLock

ReentrantLock是java.util.concurrent(JUC)中包含的锁

public class LockExample{
	private Lock lock=new ReentranrLock();
	public void func(){
			lock.lock();
			try{
				for(int i=0;i,10;i++){
				}
			}finally{
				lock.unlock();//确保释放锁
			}
}
}

public static void main(String[] args){
	LockExample lockExamle=new LockExample();
	ExecutorService executorService=Executors.new CachedThraeadPool();
	executorService.execute(()->lockExample.func());
  executorService.execute(()->lockExample.func());
}

比较

锁的实现

synchronized是JVM实现的,而ReentrantLock是JDK实现的

性能

新版本java对synchronized进行许多优化

等待可中断

当只有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情

ReentrantLock可中断,synchronized不行

公平锁

synchronized是非公平的,ReentrantLock默认情况也是非公平的,但是也可以是公平的

锁绑定多个条件

一个ReentrantLock可以同时绑定多个Condition对象

使用选择

除非要使用ReentrantLock的高级功能,否则优先使用synchronized。JVM原生支持synchronized,而ReentrantLock不是所有的JDK版本都支持。

使用synchronized不用担心没有释放锁而导致死锁的问题,因为JVM会确保锁的释放