简介
Java的多线程是运行在虚拟机上的用户级线程,是语言库中自带的一个线程API库。因此,我们能方便的调用它,不用考虑它是否在windows上还是linux上。
创建多线程的四种方式
- 继承
Thread
类,重写run()
方法 - 实现
Runnable
接口,重写run()
方法,这种方法没有返回值。 - 实现
Callable
接口,重写call()
方法。这种方法有返回值也可以抛出异常。 - 线程池,这个方法移步我的另一篇博客"Java线程池"来介绍。
继承Thread类
class MyThread extends Thread{
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
for(int i = 0 ; i < 50 ; i++) {
System.out.println(this.name + "正在工作中……" + i);
}
}
}
//执行:
MyThread t = new MyThread();
t.start();
注意,不能直接调用重写的run()
方法,这就是普通的函数调用了。
实现Runnable接口
关于Runnable
接口,其为函数式接口,这个接口的具体定义为:
@FunctionalInterface
public interface Runnable{
public void run();
}
因此我们很容易利用lambda
表达式来实现这个接口。
实现Runnable
接口
public class MyThread implements Runnable{
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
for(int i = 0 ; i<50 ;i++) {
System.out.println(this.name + " 正在执行中……" + i);
}
}
}
匿名内部类重写
Thread t = new Thread(new Runnable(){
@Override
public void run(){
for(int i = 0 ; i<50 ;i++) {
System.out.println(this.name + " 正在执行中……" + i);
}
}
});
t.start();
推荐下面这种方式。因为代码通俗易懂且清晰。同样借助于Thread类来调用start()
方法。
实现Callable接口
Callable接口也是一个函数式的接口。其定义为:
@FunctionalIterface
public interface Callable<T>{
public T call() throws Exception;
}
首先我们得创建个类实现Callable类。
public class MyThread implements Callable<Integer>{
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public Integer call(){
Integer sum = 0;
for(int i = 0 ; i < 500;i++) {
System.out.println(this.name + i);
sum += i;
}
return sum;
}
}
想要执行call()方法,必须要有FutureTask类来包裹。因此我们也要了解一下FutureTask类。
常用方法:
public FutureTask(Callable<T> callable) // 构造函数:接收Callable接口实例
public FutureTask(Runable runnable,T result) // 构造函数:接收Runnable接口实例,同时指定返回结果类型
public T get() throws InterruptedException,ExecutionException // 取得线程操作返回结果
最后我们要将它送进Thread类
public Thread(FutureTask<T> futuretask) //构造方法:接收FutureTask实例化对象
最后实例汇总:
public class testThread {
public static void main(String[] args) throws Exception{
// 实例化继承Callable接口的MyThread类
MyThread mt1 = new MyThread("线程一");
MyThread mt2 = new MyThread("线程二");
MyThread mt3 = new MyThread("线程三");
// FutureTask类接收继承Callable接口的MyThread的实例
FutureTask<Integer> ft1 = new FutureTask<Integer>(mt1);
FutureTask<Integer> ft2 = new FutureTask<Integer>(mt2);
FutureTask<Integer> ft3 = new FutureTask<Integer>(mt3);
// 启动多线程
new Thread(ft1).start();
new Thread(ft2).start();
new Thread(ft3).start();
System.out.println(ft1.get());
System.out.println(ft2.get());
System.out.println(ft3.get());
}
}
线程的一些常用操作
线程命名:
public Thread(Runnable runnable,String name) //构造函数:实例化线程对象,为线程对象设置名称
public final void setName(String name) // 普通函数:设置线程名字
public final String getName() // 普通函数:获取线程名字
线程休眠
public static void sleep(long millis) throws InterruptedException // 普通函数:设置休眠时间的毫秒数
public static void sleep(long millis,int nanos) throws InterruptedException // 普通函数:设置休眠毫秒数和纳秒数
线程中断与判断
// 以下均为Thread类的方法
public boolean isInterrupted() //普通函数:判断线程是否被中断
public void interrupt() //普通函数:中断线程执行
但是在实际开发中,一般不建议这样直接中断线程。一般也是设置一个标志位flag,来用来中断线程
如图:
线程强制执行:join()
join()这个方法,一般是用作线程结束之后,所有线程合并到主线程的方法。但是其本身作用是,如果此时某些线程异常重要,也就是说这个对象需要优先执行完成,则可以设置为线程强制执行,待其完成后其它线程继续执行。
俗话说:也就是插队
public final void join() throws InterruptedException //普通函数:强制执行
具体效果:
public class MyThread implements Runnable{
private Thread thread = null;
public MyThread() {}
public MyThread(Thread thread) {
this.thread = thread;
}
@Override
public void run() {
for(int i = 0; i<50 ; i++) {
if(i >= 20 && i <= 25) {
try {
System.out.println(thread.getName() + "被迫参与 " + Thread.currentThread().getName() + " 的工作了……" +i);
thread.join();
}catch(InterruptedException e) {
e.printStackTrace();
}
}
// 以下语句不管上面判断语句是否执行都会执行的
System.out.println(Thread.currentThread().getName() + " 正在工作中……" +i);
}
}
}
观察线程强制执行操作:
public class ThreadDemo {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
MyThread mt1 = new MyThread(mainThread);
Thread thread1 = new Thread(mt1,"子线程");
thread1.start();
for(int i = 0 ;i<20;i++) {
try {
Thread.sleep(1000); // 每次main线程休眠1秒
System.out.println(Thread.currentThread().getName() + "正在工作中……" + i);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("我main线程已经完成了所有任务,从此无法再复生了……");
}
}
运行情况:
线程让步
public static void yield() // 静态函数:线程让步
观察线程的状态
public State getState(); //得到线程实例对应的线程状态
线程一共有六个状态:
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE,
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED,
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called {@code Object.wait()}
* on an object is waiting for another thread to call
* {@code Object.notify()} or {@code Object.notifyAll()} on
* that object. A thread that has called {@code Thread.join()}
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
线程同步
操作方式:
1.同步块:
synchronized(需要同步的对象){
//需要同步的操作
}
2.方法上面加上同步
//修饰符 synchronized 返回类型 函数名()
public synchronized void test();
注意synchronize块一定要包裹需要增删改的变量!!!
3.lock对象
private final ReentrantLock lock = new ReentrantLock();
...
lock.lock();
...
lock.unlock();
注意,有try-catch-finally代码块的时候,lock必须包裹在finally代码里面。
关于Object类的自带wait方法和notify方法
public final void wait() throws InterruptedException //使线程等待,并释放锁
public final native void wait(long timeout) throws InterruptedException //使线程等待若干毫秒,并释放锁
public final native void notify(); //通知唤醒某一个处于等待的线程
public final native void notifyAll(); //通知唤醒所有处于等待的线程
常见的同步问题有:吸烟者问题、生产者消费者问题、哲学家进餐问题、读写者问题。这些可以参考《现代操作系统》这本书去学习基础知识。
后台守护线程
守护线程(Daemon)是一种运行在后台的线程服务线程,当用户线程存在时,守护线程可以同时存在,但是,当用户线程不存在时,守护线程会全部消失。其线程是为了维护整个线程所存在的。能对这些线程进行一些管理和维护。
主要操作方法:
public final setDaemon(boolean on) throws Exception // 普通函数:是否设置为守护线程
public final boolean isDaemon() //普通函数: 判断是否为
代码以及效果
class MyThread implements Runnable{
private int times;
public MyThread(int times) {
this.times = times;
}
@Override
public void run() {
for(int i = 0 ; i<times;i++) {
if(Thread.currentThread().isDaemon()) {
try {
Thread.sleep(10); // 如果是守护线程,则休眠0.01秒钟
}catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 正在工作中……"+i);
}
}
}
public class testDemo {
public static void main(String[] args) {
MyThread mt1 = new MyThread(4);
MyThread mt2 = new MyThread(100); //守护线程的循环次数远多于用户线程
Thread thread1 = new Thread(mt1,"用户线程");
Thread thread2 = new Thread(mt2,"守护线程");
thread2.setDaemon(true); //thread2设置为守护线程
thread1.start();
thread2.start();
}
}