Java多线程

简介

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

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值