// 通过Thread的静态方法拿到当前线程
System.out.println(Thread.currentThread().getName()+“执行了”+i);
}
}
}
实现Runnable
// 实现Runnable接口
class MyThreadByRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
// 不能用this了
System.out.println(Thread.currentThread().getName() + “执行了” + i);
}
}
}
public class Demo_02_02_1_ThreadCreateWays {
public static void main(String[] args) {
// 实现Runnable接口的方式启动线程
Thread thread = new Thread(new MyThreadByRunnable());
thread.start();
for (int i = 0; i < 10; i++) {
// 通过Thread的静态方法拿到当前线程
System.out.println(Thread.currentThread().getName() + “执行了” + i);
}
}
}
因为Runnable是函数式接口,用lamba也可以
new Thread(() -> {
System.out.println(“Runnable是函数式接口, java8也可以使用lamba”);
}).start();
使用Callable和Future
// 使用Callable
class MyThreadByCallable implements Callable {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+“执行了”+i);
sum+=i;
}
return sum;
}
}
public class Demo_02_02_1_ThreadCreateWays {
public static void main(String[] args) {
// 用FutureTask包一层
FutureTask futureTask = new FutureTask<>(new MyThreadByCallable());
new Thread(futureTask).start();
try {
// 调用futureTask的get能拿到返回的值
System.out.println(futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
这是最复杂的一种方式,他可以有返回值,归纳一下步骤:
-
搞一个类实现
Callable
接口,重写call
方法,在call
执行任务 -
用
FutureTask
包装实现Callable
接口类的实例 -
将
FutureTask
的实例作为Thread
构造参数 -
调用
FutureTask
实例的get
拿到返回值,调这一句会阻塞父线程
Callable
也是函数式接口,所以也能用lamba
为啥Thread构造里边能放Runnable,也能放FutureTask? 其实FutureTask继承RunnableFuture,而RunnableFuture继承Runnable和Future,所以FutureTask也是Runnable
三种方式比较
======
| 方式 | 使用简易程度 | 是否可以共享任务代码 | 是否可以有返回值 | 是否可以声明抛出异常 | 是否可以再继承别的类 |
| — | — | — | — | — | — |
| 继承Thread | 简单 | 不能 | 不能 | 不能 | 不能 |
| Runnable | 中等 | 可以 | 不能 | 不能 | 可以 |
| Callable | 复杂 | 可以 | 可以 | 可以 | 可以 |
继承Thread
是最容易的,但是也是最不灵活的
使用Callable
时最复杂的,但是也是最灵活的
这里说的共享任务代码
举个例子:
还是上面那个MyThreadByRunnable
类
MyThreadByRunnable myThreadByRunnable = new MyThreadByRunnable();
Thread thread = new Thread(myThreadByRunnable);
thread.start();
// 再来一个,复用了任务代码,继承Thread就不行
Thread thread2 = new Thread(myThreadByRunnable);
thread2.start();
线程间通信
=====
线程开始运行,拥有自己的栈空间,就如同一个脚本一样,按照既定的代码一步一步地执行,直到终止。但是,每个运行中的线程,如果仅仅是孤立地运行,那么没有一点儿价值,或者说价值很少,如果多个线程能够相互配合完成工作,这将会带来巨大的价值。
volatile和synchronized关键字
Java支持多个线程同时访问一个对象或者对象的成员变量,由于每个线程可以拥有这个变量的拷贝(虽然对象以及成员变量分配的内存是在共享内存中的,但是每个执行的线程还是可以拥有一份拷贝,这样做的目的是加速程序的执行,这是现代多核处理器的一个显著特性),所以程序在执行过程中,一个线程看到的变量并不一定是最新的。
关键字volatile可以用来修饰字段(成员变量),就是告知程序任何对该变量的访问均需要从共享内存中获取,而对它的改变必须同步刷新回共享内存,它能保证所有线程对变量访问的可见性。
关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性。
通过使用javap工具查看生成的class文件信息来分析synchronized关键字的实现细节,代码如下
public class Synchronized {
public static void main(String[] args) {
synchronized (Synchronized.class){
m();
}
}
public static synchronized void m(){
}
}
对于同步块的实现使用了monitorenter和monitorexit指令,而同步方法则是依赖方法修饰符上的ACC_SYNCHRONIZED来完成。无论采用哪种方式,其本质是对一个对象的监视器进行获取,而这个获取过程是排他的,也就是同一时刻只能有一个线程获取到由synchronized所保护对象的监视器。
任意一个对象都拥有自己的监视器,当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取到该对象的监视器才能进入同步块或者同步方法,而没有获取到监视器(执行该方法)的线程将会被阻塞在同步块和同步方法的入口处,进入BLOCKED状态。
控制线程
====
join
主线程join一个线程,那么主线程会阻塞直到join进来的线程执行完,主线程继续执行, join如果带超时时间的话,那么如果超时的话主线程也会不再等join进去的线程而继续执行.
join实际就是判断join进来的线程存活状态,如果活着就调用wait(0),如果带超时时间了的话,wait里边的时间会算出来
while (isAlive()) {
wait(0);
}
API
-
public final void join() throws InterruptedException
-
public final synchronized void join(long millis, int nanos)
-
public final synchronized void join(long millis)
例子
public class Demo_02_06_1_join extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(this.getName() + " " + i);
}
}
public static void main(String[] args) throws InterruptedException {
Demo_02_06_1_join joinThread = new Demo_02_06_1_join();
for (int i = 0; i < 100; i++) {
if (i == 10) {
joinThread.start();
joinThread.join();
}
// 打到9就停了,然后执行joinThread这里边的代码,完事继续从10打
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
sleep
睡觉方法,使得线程暂停一段时间,进入阻塞状态。
API
-
public static native void sleep(long millis) throws InterruptedException
-
public static void sleep(long millis, int nanos) throws InterruptedException
示例
public class Demo_02_06_2_sleep extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (i == 5) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 输出到4停止, 5秒后继续
System.out.println(this.getName() + " " + i);
}
}
public static void main(String[] args) throws InterruptedException {
Demo_02_06_2_sleep sleepThread = new Demo_02_06_2_sleep();
sleepThread.start();
}
}
最后的话
无论是哪家公司,都很重视Spring框架技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。
同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,好了希望这篇文章对大家有帮助!
部分截图:
加入社区:https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0
.println(this.getName() + " " + i);
}
}
public static void main(String[] args) throws InterruptedException {
Demo_02_06_2_sleep sleepThread = new Demo_02_06_2_sleep();
sleepThread.start();
}
}
最后的话
无论是哪家公司,都很重视Spring框架技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。
同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,好了希望这篇文章对大家有帮助!
部分截图:
[外链图片转存中…(img-jj1JFEug-1725667199201)]
加入社区:https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0