本文主要是介绍Java多线程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
线程创建方式
- 继承Thread类:实现起来简单,而且要获取当前线程,无需调用Thread.currentThread()方法,直接使用this即可获取当前线程,线程类已经继承Thread类了,就不能再继承其他类,多个线程不能共享同一份资源(如前面分析的成员变量 i )
- 实现Runnable接口:线程类只是实现了接口,还可以继承其他类,多个线程可以使用同一个target对象,适合多个线程处理同一份资源的情况。要访问当前线程,必须调用Thread.currentThread()方法。
- 通过Callable和Future接口创建线程:上述两种方法都不能有返回值,且不能声明抛出异常。而Callable接口则实现了此两点,Callable接口如同Runable接口的升级版,其提供的call()方法将作为线程的执行体,同时允许有返回值。
Future
- get:方法可以当任务结束后返回一个结果,如果调用时,工作还没有结束,则会阻塞线程,直到任务执行完毕
- get(long timeout,TimeUnit unit):做多等待timeout的时间就会返回结果
- cancel(boolean mayInterruptIfRunning):方法可以用来停止一个任务,如果任务可以停止(通过mayInterruptIfRunning来进行判断),则可以返回true,如果任务已经完成或者已经停止,或者这个任务无法停止,则会返回false.
- isDone():方法判断当前方法是否完成
- isCancel():方法判断当前方法是否取消
Callable Future示例
public class FutureTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
}
class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("call");
return "MyCallable";
}
}
改变线程状态
- Thread.sleep(long millis) 线程调用此方法,则进入RIMED.WAITING状态,但不释放锁,millis后线程自动苏醒进入就绪状态。作用:给其他线程执行的最佳机会。
- Thread.yield():线程调用此方法,当前线程放弃获取的CPU时间片,但不释放资源,由运行状态变为就绪状态,让OS再次选择线程。作用:让相同优先级的线程轮流执行,但并不一定会轮流执行,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中,Thread.yield()不会阻塞线程。该方法sleep()类似,只是不能由用户指定暂停时长。
- join()/join(long millis):当前线程调用其他线程的join()方法,当前线程进入WAITING/TIMED_WAITING状态,当前线程不会释放已经持有的对象锁,线程执行完或millis时间到,当前线程进入就绪状态。
- wait:当前线程调用对象wait(),当前线程释放对象锁,进入等待队列。依靠notfiy()/notifyAll()唤醒或wait(long millis)时间到自然醒。
- notfiy:唤醒此对象监视器上等待的单个线程,选择是任意。
- notfiyAll:唤醒此对象监视器上等待的所有线程。
join 示例
public class Join {
public static void main(String[] args) {
Object object = new Object();
MyThread myThread = new MyThread("myThread", object);
myThread.start();
synchronized (myThread) {
for (int i = 0; i < 50; i++) {
if (i == 20) {
try {
myThread.join();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "--" + i);
}
}
}
}
class MyThread extends Thread {
private String name;
private Object lock;
public MyThread(String name, Object object) {
this.name = name;
this.lock = object;
}
@Override
public void run() {
synchronized (this) {
for (int i = 0; i < 20; i++) {
System.out.println(name + "-" + i);
}
}
}
}
wait notifyAll 示例
public class WaitAndNotify {
public static void main(String[] args) {
MethodClass methodClass = new MethodClass();
Thread thread1 = new Thread(() -> {
try {
methodClass.product();
} catch (Exception e) {
e.printStackTrace();
}
}, "thread1");
Thread thread2 = new Thread(() -> {
try {
methodClass.customer();
} catch (Exception e) {
e.printStackTrace();
}
}, "thread2");
Thread thread3 = new Thread(() -> {
try {
methodClass.customer();
} catch (Exception e) {
e.printStackTrace();
}
}, "thread3");
thread1.start();
thread2.start();
thread3.start();
}
}
class MethodClass {
final int MAX_COUNT = 20;
int PRODUCT_COUNT = 0;
public synchronized void product() throws Exception {
while (true) {
System.out.println(Thread.currentThread().getName() + "::run::" + PRODUCT_COUNT);
Thread.sleep(100);
if (PRODUCT_COUNT >= MAX_COUNT) {
System.out.println("暂停生产");
wait();
} else {
PRODUCT_COUNT++;
}
notifyAll();
}
}
public synchronized void customer() throws Exception {
while (true) {
System.out.println(Thread.currentThread().getName() + "::run::" + PRODUCT_COUNT);
Thread.sleep(100);
if (PRODUCT_COUNT <= 0) {
System.out.println("暂停消费");
wait();
} else {
PRODUCT_COUNT--;
}
notifyAll();
}
}
}
volatile 关键字
- 在多线程环境下使用volatile变量能够保证每次读取前必须先从主内存刷新最新的值,每次写入后必须立即同步回主内存当中。也就是说,volatile关键字修饰的变量看到的随时是自己的最新值。但是volatile关键词不能保证原子性。
- 每次读、取强制从主内存刷数据,能防止字节码指令重排。
- 适用单线程写,多线程读。
- 原则:能不用就不用,不确定的时候也不用。
- 替代方案:Atomic原子操作类。
synchronized 关键字
- 锁对象:锁当前对象
- 锁方法:锁调用方的对象
- 锁代码块:锁调用方的对象(推荐,最小粒度)
这篇关于Java多线程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!