一、线程和进程的区别
进程是执行者的应用程序,是操作系统资源分配和独立运行的基本单位。而线程是进程内部的一个执行序列,同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器,线程切换开销小。
进程是程序的一种动态形式,是CPU、内存等资源占用的基本单位,而线程是不能占有这些资源的。
二、线程的可用状态
1、新建new
2、可运行/就绪 runnable:线程对象创建后,其他线程调用了该对象的start方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu的使用权。
3、运行running,可运行状态的线程获得cpu时间片后,执行程序代码。
4、阻塞 block
等待阻塞:运行的程序执行wait(),JVM会把该线程放入等待序列中。
同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,JVM会把该线程放入锁池。
其他阻塞:运行的线程执行Thread.sleep()或t.join()方法时,或者发出I/O请求时,JVM会把该线程置为阻塞状态。等这些方法超时时,会重新转入可运行状态runnable。
5、死亡dead:线程运行run()、mian()方法执行结束,或者因异常退出了run(),方法时,则该线程结束生命周期。死亡的线程不可再复生。

●死锁的概念:两个或两个以上的进程(线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外部处理作用,它们都将无限等待下去。
产生条件:
·互斥条件:进程要求对所分配的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时其它进程请求进程(线程)只能等待。
·不剥夺条件:进程所获得的资源在未使用完毕之前,不能被其它进程强行夺走,即只能该资源的进程自己来释放。
·请求和保持条件:进程已经保持了至少一个资源,但又提出新的资源请求,而该资源已被其它进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。
·循环等待条件:存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被链中下一个进程所请求。
三、多线程的基本概念
1、继承Thread类实现多线程
线程的主体类继承Thread类,而线程启动的主方法需要覆写Thread类中的run()方法。
package com.multithread.learning;
public class Thread1 extends Thread{
private String name;
public Thread1(String name){
this.name = name;
}
public void run(){
for(int i = 0; i < 5; i++){
System.out.println(name+"运行:"+i);
try {
sleep((long) (Math.random()*10));
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
package com.multithread.learning;
public class Main {
public static void main(String[] args) {
Thread1 mTh1 = new Thread1("A");
Thread1 mTh2 = new Thread1("B");
mTh1.start();
mTh2.start();
}
}
A运行:0
B运行:0
A运行:1
B运行:1
A运行:2
B运行:2
B运行:3
B运行:4
A运行:3
A运行:4
执行会发现,A,B会无规律交替执行,且每次执行结果都是不一定一样的。
2、实现Runnable接口实现多线程
使用Thread类的确可以方便的进行多线程的实现,但是这种方式最大的缺点就是单继承的问题。
package com.multithread.learning;
class MyThreadRunnable implements Runnable { // 线程的主体类
private String title;
public MyThreadRunnable(String title) {
this.title = title;
}
@Override
public void run() { // 线程的主方法
for (int x = 0; x < 10; x++) {
System.out.println(this.title + "运行,x = " + x);
}
}
}
package com.multithread.learning;
public class Main {
public static void main(String[] args) {
MyThreadRunnable myThread1 = new MyThreadRunnable("A");
MyThreadRunnable myThread2 = new MyThreadRunnable("B");
MyThreadRunnable myThread3 = new MyThreadRunnable("C");
new Thread(myThread1).start();
new Thread(myThread2).start();
new Thread(myThread3).start();
}
}
A运行,x = 0
B运行,x = 0
C运行,x = 0
B运行,x = 1
A运行,x = 1
B运行,x = 2
C运行,x = 1
B运行,x = 3
A运行,x = 2
B运行,x = 4
B运行,x = 5
C运行,x = 2
C运行,x = 3
C运行,x = 4
C运行,x = 5
C运行,x = 6
.
.
要启动多线程依靠Thread类的start()方法完成,之前继承Thread类的时候可以将此方法直接继承过来使用,但现在实现的是Runnable接口,没有这个方法可以继承,因此需要依靠Thread类中的 一个构造方法:public Thread(Runnable target),来接收Runnable接口对象。
3、实现callable接口
主体类实现callable,使用FutureTask来接收结果
package com.multithread.learning;
import java.util.concurrent.Callable;
public class CallableTest implements Callable<Integer>{
private String name;
public CallableTest(String name){
this.name = name;
}
@Override
public Integer call() throws Exception {
int sum = 0 ;
for (int i = 0; i <= 100; i++){
System.out.println(name +"线程:"+i);
sum +=i;
}
return sum;
}
}
package com.multithread.learning;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MainCallable {
public static void main(String[] args) {
CallableTest th1 = new CallableTest("A");
CallableTest th2 = new CallableTest("B");
//1.执行callable方式,需要futureTask实现类的支持,用于接受运算结果
FutureTask<Integer> result1 = new FutureTask<>(th1);
FutureTask<Integer> result2 = new FutureTask<>(th2);
new Thread(result1).start();
new Thread(result2).start();
Integer sum;
try {
sum = result1.get();
System.out.println("-----------");
System.out.println(sum);
sum = 0;
sum = result2.get();
System.out.println("-----------");
System.out.println(sum);
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
A线程:94
A线程:95
A线程:96
A线程:97
A线程:98
A线程:99
A线程:100
B线程:95
B线程:96
B线程:97
B线程:98
B线程:99
B线程:100
-----------
5050
-----------
5050
callable和runnable接口的区别:
(1)Callable规定的方法是call(),而Runnable规定的方法是run().
(2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值的。
(3)call()方法可抛出异常,而run()方法是不能抛出异常的。
(4)运行Callable任务可拿到一个Future对象, Future表示异步计算的结果。
它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。
四、线程调度
1、线程的优先级
Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:
static int MAX_PRIORITY
线程可以具有的最高优先级,取值为10。
static int MIN_PRIORITY
线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY
分配给线程的默认优先级,取值为5。
Thread类的setPriority()和getPriority()方法分别用来设置和获取线程的优先级。
package com.multithread.learning;
public class PriorityTest implements Runnable {
@Override
public void run() {
for (int x = 0; x < 5; x++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",x=" + x);
}
}
}
package com.multithread.learning;
public class MainPriorityTest {
public static void main(String[] args) {
PriorityTest th = new PriorityTest();
Thread t1 = new Thread(th,"线程A") ;
Thread t2 = new Thread(th,"线程B") ;
Thread t3 = new Thread(th,"线程C") ;
t1.setPriority(Thread.MIN_PRIORITY);
t2.setPriority(Thread.MAX_PRIORITY);
t3.setPriority(Thread.MIN_PRIORITY);
t1.start();
t2.start();
t3.start();
}
}
线程B,x=0
线程C,x=0
线程A,x=0
线程C,x=1
线程A,x=1
线程B,x=1
线程B,x=2
线程A,x=2
线程C,x=2
线程B,x=3
线程A,x=3
线程C,x=3
线程A,x=4
线程C,x=4
线程B,x=4
执行可以发现,B始终是在A,C前面执行
2、线程睡眠
Thread.sleep(long millis)方法,是线程转到阻塞状态,此方法不释放对象锁,当睡眠结束后,就转为就绪状态。
3、线程等待
wait()方法,导致当前线程等待,直到其他线程调用此对象的notify()方法和notifuAll()唤醒方法。这两个唤醒方法也是Object类中的方法。
4、线程让步
Thread.yield()方法,暂停当前正在执行的线程对象,把执行机会让给相同或者更高优先级的线程
5、线程加入
join()方法,等待其他线程终止,在当前线程中调用另一个线程的join()方法,则当前线程转入阻塞状态,直到另一个进程运行结束,当前线程再由阻塞转为就绪状态
6、线程唤醒
Object类中的notify()方法,唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。 直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。类似的方法还有一个notifyAll(),唤醒在此对象监视器上等待的所有线程。
网友评论