Java线程具有五种基本状态
1.等待阻塞 – 运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
package com.demo.test;
public class MyThread extends Thread {
private String name;
public MyThread(String name){
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(name + "运行 :" + i);
}
}
}
package com.demo.test;
public class ThreadTest {
public static void main(String[] args) {
Thread myThread1 = new MyThread("A"); // 创建一个新的线程 myThread1 此线程进入新建状态
Thread myThread2 = new MyThread("B"); // 创建一个新的线程 myThread2 此线程进入新建状态
myThread1.start(); // 调用start()方法使得线程进入就绪状态
myThread2.start(); // 调用start()方法使得线程进入就绪状态
}
}
A运行 :0
B运行 :0
B运行 :1
B运行 :2
B运行 :3
B运行 :4
B运行 :5
B运行 :6
B运行 :7
B运行 :8
A运行 :1
A运行 :2
A运行 :3
B运行 :9
A运行 :4
A运行 :5
A运行 :6
A运行 :7
A运行 :8
A运行 :9
package com.demo.test;
public class MyRunnable implements Runnable{
private String name;
public MyRunnable(String name){
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(name + "运行 :" + i);
}
}
}
package com.demo.test;
public class ThreadTest {
public static void main(String[] args) {
Runnable myRunnable = new MyRunnable("A"); // 创建一个Runnable实现类的对象
Thread thread1 = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
Runnable myRunnable1 = new MyRunnable("B");
Thread thread2 = new Thread(myRunnable1);
thread1.start(); // 调用start()方法使得线程进入就绪状态
thread2.start();
}
}
A运行 :0
B运行 :0
B运行 :1
B运行 :2
A运行 :1
A运行 :2
B运行 :3
A运行 :3
A运行 :4
B运行 :4
A运行 :5
A运行 :6
A运行 :7
B运行 :5
A运行 :8
A运行 :9
B运行 :6
B运行 :7
B运行 :8
B运行 :9
package pers.hwh.thread;
import java.util.concurrent.Callable;
public class MyCallable implements Callable {
//与run()方法不同的是,call()方法具有返回值
@Override
public Object call() throws Exception {
System.out.println("子线程正在计算:");
Thread.sleep(3000);
int sum=0;
for (int i = 0; i < 100; i++) {
sum+=i;
}
return sum;
}
}
package pers.hwh.thread;
import java.util.concurrent.*;
public class CallableTest {
public static void main(String[] args) {
Callable<Integer> myCallable = new MyCallable();
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable);
Thread th = new Thread(ft);
th.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程正在执行");
try {
int sum = ft.get();
System.out.println("运行结果:sum="+sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}//获取运行结果
System.out.println("所有任务执行完毕!");
}
}
task运行结果,sum = 4950
public class FutureTask<V> implements RunnableFuture<V>
1
FutureTask类实现了RunnableFuture接口,我们看一下RunnableFuture接口的实现:
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
Runnable 与 Callable的区别
Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取到结果;当不调用此方法时,主线程不会阻塞!
join —— 让一个线程等待另一个线程完成才继续执行。如A线程执行体中调用B线程的join()方法,则A线程被阻塞,直到B线程执行完为止,A才能得以继续执行。join()方法,等待其他线程终止。在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个线程运行结束,当前线程再由阻塞转为就绪状态。join是Thread类的一个方法,启动线程后直接调用,join() 的作用:让“主线程”等待“子线程”结束之后才能继续运行。
package com.demo.test;
public class Thread1 extends Thread{
private String name;
public Thread1(String name) {
super(name);
this.name=name;
}
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程运行开始!");
for (int i = 0; i < 5; i++) {
System.out.println("子线程"+name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程运行结束!");
}
}
package com.demo.test;
public class Main {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"主线程运行开始!");
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");
}
}
main主线程运行开始!
main主线程运行结束!
A 线程运行开始!
B 线程运行开始!
B 线程运行结束!
A 线程运行结束!
package com.demo.test;
public class Main {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName()+"主线程运行开始!");
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
try {
mTh1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
mTh2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+ "主线程运行结束!");
}
}
main主线程运行开始!
A 线程运行开始!
B 线程运行开始!
A 线程运行结束!
B 线程运行结束!
main主线程运行结束!
sleep()是Thread类的静态方法。该方法声明抛出了InterrupedException异常。所以使用时,要么捕捉,要么声明抛出。有两种重载方式:
static void sleep(long millis); //让当前正在执行的线程暂停millis毫秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的精度和准度的影响。
static void sleep(long millis , int nanos); //让当前正在执行的线程暂停millis毫秒加nanos微秒,并进入阻塞状态,该方法受到系统计时器和线程调度器的
sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。常用来暂停程序的运行。同时注意,sleep()方法不会释放锁。
yield()是Thread类的静态方法。它能让当前线程暂停,但不会阻塞该线程,而是由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执行。因此,**使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。**但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权,**也有可能是当前线程又进入到“运行状态”继续运行!**值得注意的是,yield()方法不会释放锁。
package com.demo.test;
public class ThreadYield extends Thread{
public ThreadYield(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println("" + this.getName() + "-----" + i);
// 当i为30时,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
if (i ==30) {
this.yield();
}
}
}
}
package com.demo.test;
public class Main {
public static void main(String[] args) {
ThreadYield yt1 = new ThreadYield("A");
ThreadYield yt2 = new ThreadYield("B");
yt1.start();
yt2.start();
}
}
import java.util.Date;
public class TestInterrupted implements Runnable {
public void run(){
System.out.println("开始运行run方法,时间:"+(new Date()));
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
System.out.println("线程已被唤醒!");
}
System.out.println("结束运行run方法,时间:"+(new Date()));
}
}
public class TestMainInterrupted {
public static void main(String[] args) {
TestInterrupted t = new TestInterrupted();
Thread th = new Thread(t);
th.start();
try {
th.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
th.interrupt();
}
}
1.isInterrupted()是一个实例方法,用于判断当前线程是否已经被中断,若是,则返回true,如果不是返回false。线程的中断状态不会受该方法的影响。
2.interrupted()是一个静态的方法,用于判断执行中的线程是否已经被中断,若是,返回true,否则返回false,调用此方法后,线程的中断状态将被消除,即处于中断状态的线程将变为非中断状态。
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据不准确,相互之间产生冲突,因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
① 原理
==不同线程对同步锁的访问是互斥的。也就是说,某时间点,对象的同步锁只能被一个线程获取到!==通过同步锁,我们就能在多线程中,实现对“对象/方法”的互斥访问。 例如,现在有个线程A和线程B,它们都会访问“对象obj的同步锁”。假设,在某一时刻,线程A获取到“obj的同步锁”并在执行一些操作;而此时,线程B也企图获取“obj的同步锁” —— 线程B会获取失败,它必须等待,直到线程A释放了“该对象的同步锁”之后线程B才能获取到“obj的同步锁”从而才可以运行。
② 基本规则
③ 实例锁和全局锁
public synchronized void run() {
// ....有时间再补充具体例子
}
synchronized (obj) {
//...有时间再补充具体例子
}
wait():导致当前线程等待并使其进入到等待阻塞状态。直到其他线程调用该同步锁对象的notify()或notifyAll()方法来唤醒此线程。
public final void wait() throws InterruptedException
public final void wait(long timeout) throws InterruptedException
//指定等待时长,毫秒为单位
public final void wait(long timeout,int nanos) throws InterruptedException
//指定等待时长,毫秒+纳秒,timeout毫秒为单位,nanos纳秒为单位。时间精度更高
notify():唤醒在此同步锁对象上等待的单个线程,如果有多个线程都在此同步锁对象上等待,则会任意选择其中某个线程进行唤醒操作,只有当前线程放弃对同步锁对象的锁定,才可能执行被唤醒的线程。
public final void notify()
notifyAll():唤醒在此同步锁对象上等待的所有线程,只有当前线程放弃对同步锁对象的锁定,才可能执行被唤醒的线程。
public final void notify()
1、系统资源的竞争
2、进程推进顺序非法
Java中死锁最简单的情况是,一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得锁L1,因为默认的锁申请操作都是阻塞的,所以线程T1和T2永远被阻塞了。导致了死锁。这是最容易理解也是最简单的死锁的形式。但是实际环境中的死锁往往比这个复杂的多。可能会有多个线程形成了一个死锁的环路,比如:线程T1持有锁L1并且申请获得锁L2,而线程T2持有锁L2并且申请获得锁L3,而线程T3持有锁L3并且申请获得锁L1,这样导致了一个锁依赖的环路:T1依赖T2的锁L2,T2依赖T3的锁L3,而T3依赖T1的锁L1。从而导致了死锁。