创建线程的三种方式
1、继承Thread;
static class MyThread extends Thread{@Overridepublic void run() {//do something...}
}
public static void main(String[] args) throws InterruptedException {MyThread thread = new MyThread ();thread.start();
}
2、实现Runnable接口(该接口无返回值);
static class MyRunnable implements Runnable {public void run() {//do something...}
}
public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(new MyRunnable(),"MyRunnable");thread.start();
}
3、实现Callable接口(该接口有返回值,在之前的SpringMvc中有提到过)
static class MyCallable implements Callable<String> {public String run() {String result = "";//do something...return result;}
}
public static void main(String[] args) throws InterruptedException {FutureTask<String> task = new FutureTask<String>(MyCallable);new Thread(task).start();//获取结果值String result = task.get();
}
jdk也提供了通过了创建线程池的方式去创建线程,但是线程池底层及时通过去创建Thread对象创建线程的,所以只有这三种方式
线程共享协作
先来认识一下 synchronized 这个关键字,这个在大部分网上都能找到相关的资料,这里就简单的说明一下:
synchronized锁也叫内置锁,可用于两种情况,1、修饰在静态方法上,即类锁;2、修饰在普通方法上或者synchronized代码块,即对象锁。示例如下:
//类锁:
public synchronized static void function(){//do something...}
//对象锁:
//修饰方法,同一个对象同一时刻只能有一个线程执行该方法
public synchronized void function(){...}
//synchronized 代码快
public void function(){synchronized(this){//锁住当前对象(this可替换成其他对象,即只有执行完当前代码块后,才能释放,同一时刻,当前线程获取到对象锁后,才能执行,其他线程进入等待阻塞状态)//...}
}
volatile关键字:适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性。
ThreadLocal:可以参考ThreadLocal详解,这篇讲的挺详细的
wait(),notify()/notifyAll()
wait():即等待通知,作用于对象上,当前线程调用对象的wait()方法会使进入阻塞状态并释放当前持有锁,需和synchronized一起使用,等待当前对象调用notify/notifyAll()方法之后,需重新竞争到锁后,方可继续执行,未被唤醒则一直等待。
notify()/notifyAll():作用于对象上,用来唤醒阻塞状态的wait()方法,需和synchronized一起使用,执行完后,需继续执行后面的程序才能释放当前持有锁,一般将该方法放于最后。
public void function () {//...synchronized (this) {//do something...notify();}//...
}
notify()唤醒随机一个wait()方法阻塞线程,而notifyAll()则是唤醒所有的wait()阻塞线程,wait()被唤醒后,需重新去竞争锁,才能继续执行。
wait(long mills):即等待超时方法,即等待mills毫秒后,如若时间到后未收到唤醒,则继续执行后面的代码。
long overtime = now+T;
long remain = T;//等待的持续时间
while(result不满足条件&& remain>0){wait(remain);remain = overtime – now;//等待剩下的持续时间
}
return result;
join()
可以将并行执行的线程变成串行执行模式,可以理解为插队执行,以下为示例:
static class Worker implements Runnable{private Thread thread;public Worker(Thread thread) {this.thread = thread;}public void run() {try {System.out.println(thread.getName()+"start join...");thread.join();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+" terminte..." + System.currentTimeMillis());}}public static void main(String[] args) throws InterruptedException {System.out.println(Thread.currentThread().getName()+" start..." + System.currentTimeMillis());Thread main = Thread.currentThread();Thread thread1 = new Thread(new Worker(main),"join1");Thread thread2 = new Thread(new Worker(thread1),"join2");Thread thread3 = new Thread(new Worker(thread2),"join3");thread1.start();thread2.start();thread3.start();System.out.println(Thread.currentThread().getName()+" sleep...");try {Thread.sleep(1000);System.out.println(Thread.currentThread().getName()+" end...");} catch (InterruptedException e) {e.printStackTrace();}}
执行结果为:
main start...1616734827339
main sleep...
mainstart join...
join2start join...
join1start join...
main end...
join1 terminte...1616734828340
join2 terminte...1616734828340
join3 terminte...1616734828340
即在当前线程中调用其他线程的join()方法后,其他线程就会进去到阻塞状态,只有等待当前线程执行完成后,其他线程才能继续执行,由并行变成串行;
join()方法里面也是由wait()方法去处理的