Java多线程以及线程状态转换
Java实现多线程的方式:
1 继承Thread类,重写run方法。
class MyThread extends Thread{
public void run(){
System.out.println(Thread.currentThread().getName()+"MyThread.run()!!");
}
}
public class testThread {
public static void main(String [] args){
MyThread myThread = new MyThread();
myThread.start();
}
}
2 实现Runnable接口,重写该接口的run方法。
接口的实现类的实例作为Thread的target作为参数传入带参的Thread构造函数,通过调用start()方法启动线程。
class RunnableDemo implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"Runnable.run()!!");
}
}
public class testThread {
public static void main(String [] args){
Thread thread = new Thread(new RunnableDemo());
thread.start();
}
}
3 实现callable接口,重写call()方法
创建Callable接口的实现类,并实现Call方法
创建Callable实现类的实现,使用Future Task类包装Callable对象,该FutureTask对象封装了Callable对象的Call方法的返回值
使用FutureTask对象作为Thread对象的target创建并启动线程
调用FutureTask对象的get()来获取子线程执行结束的返回值
class CallableAndFutrue implements Callable<Object> {
@Override
public Object call() throws Exception {
System.out.println(Thread .currentThread().getName()+"Callable.call!!");
return null;
}
}
public class testThread {
public static void main(String [] args){
Callable<Object> oneCallable = new CallableAndFutrue();
FutureTask<Object> oneTask = new FutureTask<>(oneCallable);
Thread thread1 = new Thread(oneTask);
thread1.start();
}
}
可以在任务结束后提供一个返回值,runnable不可以,call方法可以抛出异常,runnable的run方法不行,可以通过运行callable得到的future对象监听目标线程调用call方法的结果,得到返回值。
应用场景:
当父线程想要获得子线程的运行结果时。
Java中六种线程状态:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7HQc3stI-1572353904915)(C:\Users\Z00513~1\AppData\Local\Temp\1572233837426.png)]
1 新建(NEW)
线程创建后没有启动
2 可运行(RUNNABLE)
一旦调用了start方法,线程就处于可运行状态,可运行状态的线程可能正在运行,也可能还没有运行而是正在等待CPU时间片。
3 阻塞(BLOCKED)
处于阻塞状态的线程不会占用CPU资源,线程进入阻塞状态的情况:
(1) 等待获取锁
(2)IO阻塞
"阻塞状态"与”等待状态“的区别:”阻塞状态“在等待着获取一个排他锁,这个时间发生在另外一个线程放弃这个锁的时候;而”等待状态“则是等待一段时间,或者唤醒动作发生。在程序等待进入同步区域的时候,线程将进入这种状态。
4 无限期等待(WAITING)
处于这种状态的线程不会被分配CPU执行时间,要等待被其他线程显式唤醒。以下方法会让线程陷入无限期等等待状态:
(1)没有设置timeout参数的wait方法;
(2)没有设置timeout参数的join方法;
(3)LockSupport.park()方法;
5 限时等待(TIMED_WAITING)
处于这种状态的线程不会被分配CPU执行时间,不过无须等待被其他线程显式唤醒,在一定时间之后它们会由系统自动唤醒。以下方法会让线程进入限时等待状态:
(1)thread的sleep方法;
(2)设置了timeout参数的wait方法;
(3)设置了timeout参数的join方法;
(4)LockSupport.parkNanos();
(5)LockSupport.parkUntil();
6 死亡(TERMINATED)
已经终止线程的线程状态。
几种状态的转换:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0NcEUEkY-1572353904916)(C:\Users\Z00513~1\AppData\Local\Temp\1572233912395.png)]
https://www.cnblogs.com/paddix/p/5381958.html
wait、 notify、 notifyall方法的使用 (object类中)
1 wait 方法
将当前运行的线程挂起(让其进入阻塞状态),直到notify或notifyall方法唤醒线程。
wait方法是一个本地方法,其底层是通过一个叫做监视器锁(monitor),在调用wait方法时必须获得monitor对象的所有权,所以只能在同步的块或者方法中调用,否则会抛出java.lang.IllegalMonitorStateException异常,Java中只能通过Synchronized关键字来实现。
public synchronized void WaitTest(){
System.out.println("start---------------");
try{
wait(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("end------------------");
}
public static void main(String [] args){
final testWait testWait = new testWait();
new Thread(new Runnable() {
@Override
public void run() {
testWait.WaitTest();
}
}).start();
}
2 notify 和notifyall
用来唤醒对应对象monitor上等待的线程。两者的区别在于前者只能唤醒一个线程,对其他的线程没有影响,而notifyall则是唤醒所有的线程。
public synchronized void testWait(){
System.out.println(Thread.currentThread().getName() +" Start-----");
try {
wait(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +" End-------");
}
public static void main(String[] args) throws InterruptedException {
final testNotify test = new testNotify();
for(int i=0;i<5;i++) {
new Thread(new Runnable() {
@Override
public void run() {
test.testWait();
}
}).start();
}
synchronized (test) {
test.notify();
}
Thread.sleep(3000);
System.out.println("-----------分割线-------------");
synchronized (test) {
test.notifyAll();
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zw4jZ28h-1572353904917)(C:\Users\Z00513~1\AppData\Local\Temp\1572336874795.png)]
sleep 、join方法的使用(Thread类中)
1 sleep
作用是让当前的线程暂停指定的时间,和wait的区别在于:wait方法依赖于同步,而sleep方法可以直接调用,sleep知识暂时让出CPU的执行权,并不释放锁,而wait需要释放锁。
public synchronized void sleepMethod(){
System.out.println("Sleep start-----");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Sleep end-----");
}
public synchronized void waitMethod(){
System.out.println("Wait start-----");
synchronized (this){
try {
wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Wait end-----");
}
public static void main(String[] args) {
final testSleep test1 = new testSleep();
for(int i = 0;i<3;i++){
new Thread(new Runnable() {
@Override
public void run() {
test1.sleepMethod();
}
}).start();
}
try {
Thread.sleep(10000);//暂停十秒,等上面程序执行完成
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("-----分割线-----");
final testSleep test2 = new testSleep();
for(int i = 0;i<3;i++){
new Thread(new Runnable() {
@Override
public void run() {
test2.waitMethod();
}
}).start();
}
}
可以看出sleep方法实现的暂停,程序是顺序进入同步块的,只有当上一个线程执行完成的时候,下一个线程才能进入同步方法,sleep暂停期间一直持有monitor对象锁,其他线程是不能进入的。而wait方法则不同,当调用wait方法后,当前线程会释放持有的monitor对象锁,因此,其他线程还可以进入到同步方法,线程被唤醒后,需要竞争锁,获取到锁之后再继续执行。
2 join
join方法的作用是父线程等待子线程执行完成后再执行,也就是相当于将异步执行的线程合并为同步的线程。join有三种实现的方式。
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " start-----");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " end------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//不使用join
public static void main(String[] args) {
for (int i=0;i<5;i++) {
Thread test = new Thread(new testJoin());
test.start();
}
System.out.println("Finished~~~");
}
//使用join
// public static void main(String[] args) {
// for (int i=0;i<5;i++) {
// Thread test = new Thread(new testJoin());
// test.start();
// try {
// test.join(); //调用join方法
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// }
//
// System.out.println("Finished~~~");
// }
synchronized原理
https://blog.csdn.net/javazejian/article/details/72828483
在 Java 中,关键字 synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能),这点确实也是很重要的。