1、进程与线程区别:
在操作系统中打开一个记事本,就是启动一个程序,代表着操作系统会分配一块内存给这个程序进程,一个进程至少有一个线程,线程是最小的执行单元,可以共享进程的数据,开销比较小;
打开一个程序就会开启一个进程,一个进至少有一个线程,多个线程之间可以共享进程的数据;
2、并发与并行的区别:
并行:是指同一时刻有多个指令在处理器上执行;是同时进行;
并发:指一个时刻只能有一个指令得到执行,但是会快速的轮换执行多个指令
我们打开自己的操作系统电脑开着音乐,开着记事本 而操作系统CPU在执行时是轮换执行各个进程的命令,只是CPU的处理速度比较快,我们感觉不到;
3、创建多线程:
方法一:继承Thread类
public class ThreadTest extends Thread {
public ThreadTest(String name) {
super(name);
}
@Override
public void run() {
for (int i=0;i<10;i++) {
System.out.println(currentThread().getName()+" is running"+i);
}
}
public static void main(String[] args) {
//1------自定义类继承Thread类
ThreadTest newThreadTest=new ThreadTest("new Thread");
//2------启动线程
newThreadTest.start();
for (int i=0;i<10;i++) {
System.out.println(currentThread().getName()+" is running"+i);
}
}
}
main is running0
main is running1
main is running2
main is running3
main is running4
main is running5
main is running6
main is running7
main is running8
main is running9
new Thread is running0
new Thread is running1
new Thread is running2
new Thread is running3
new Thread is running4
new Thread is running5
new Thread is running6
new Thread is running7
new Thread is running8
new Thread is running9
方法二:实现Runnable接口
public class InterfaceThead implements Runnable{
@Override
public void run() {
for (int i=0;i<10;i++) {
System.out.println("my thread "+" is running"+i);
}
}
public static void main(String[] args) {
//1----定义一个类实现Runnable接口并重写其中的run方法
InterfaceThead threadInterfaceThead=new InterfaceThead();
//2----将runnable作为Thread 的构造参数传入target
Thread tesThread=new Thread(threadInterfaceThead);
//3-----启动线程
tesThread.start();
}
}
my thread is running0
my thread is running1
my thread is running2
my thread is running3
my thread is running4
my thread is running5
my thread is running6
my thread is running7
my thread is running8
my thread is running9
方法三:
public class CallableThread implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("开始调用新的线程!");
return "有返回值的线程!";
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
//1-----FutureTask包装callable对象,其中callable要重写call方法
FutureTask task=new FutureTask<String>(new CallableThread());
//2-----将futureTask作为new Thread(target,name)
Thread thread=new Thread(task, "callableThread");
//3-----启动线程
thread.start();
//4-----接收返回的值
if(task.get() != null) {
System.out.println(task.get());
}
}
}
结论:无论使用哪种方式,都调用的是Thread的run方法,如果不适用方法一,不覆盖Thread的run方法的话,使用方法二与方法三,传入一个target则会调用Runable或者Callable的run方法:请查看源码Thread的run方法源码:
@Override
public void run() {
if (target != null) {
target.run();
}
}
4、线程周期:
本图片来源于网络
线程的5种状态:
新建状态:new 在使用方法new Thread()系统就会为线程分配内存,处于就绪状态;
就绪状态:runnable 使用Thread 的start的方法是线程处于就绪状态等待JVM的调用;
运行状态:run JVM开始调用该线程
阻塞状态:blocked 使其他的线程暂时获得执行的资源的机会,暂且将正在执行的线程阻塞,一般是采用的抢占式调度策略,也可以使用sleep或者yield暂且主动让出当前的资源;
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;wait会释放持有的锁
2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态:线程执行完了或者因异常退出了run()方法,该线程结束生命周期
5、控制线程:
图片来源:https://www.jianshu.com/p/3c85daeba1a5
sleep方法
/**
* SleepTest 继承线程Thread类,每1秒打印一次时间;
* 主线程sleep 9秒,
* interrupt子线程
*
*
*/
public class SleepTest extends Thread {
@Override
public void run() {
while(true) {
System.out.println("now time is "+new Date());
try {
sleep(1000);
} catch (InterruptedException e) {
System.out.println(currentThread().getName()+"is be interruped!");
return ;
}
}
}
public static void main(String[] args) {
SleepTest test=new SleepTest();
test.start();
try {
Thread.sleep(9000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(currentThread().getName()+" is running!");
test.interrupt();
}
}
now time is Thu Sep 12 15:43:34 CST 2019
now time is Thu Sep 12 15:43:35 CST 2019
now time is Thu Sep 12 15:43:36 CST 2019
now time is Thu Sep 12 15:43:37 CST 2019
now time is Thu Sep 12 15:43:38 CST 2019
now time is Thu Sep 12 15:43:39 CST 2019
now time is Thu Sep 12 15:43:40 CST 2019
now time is Thu Sep 12 15:43:41 CST 2019
now time is Thu Sep 12 15:43:42 CST 2019
main is running!
Thread-0is be interruped!
yield方法
/**
* yield方法调用后,线程会暂时处于就绪状态等待JVM的调用,可能就会立即被调用
*
*
*/
public class YieldTest extends Thread{
@Override
public void run() {
for(int i=0;i<20;i++) {
System.out.println(currentThread().getName()+" is running "+i);
if(i%2==0) {
Thread.yield();
}
}
}
public static void main(String[] args) {
YieldTest test1=new YieldTest();
YieldTest test2=new YieldTest();
test1.start();
test2.start();
}
}
Thread-0 is running 10
Thread-1 is running 8
Thread-0 is running 11
Thread-0 is running 12
Thread-1 is running 9
Thread-1 is running 10
Thread-0 is running 13
Thread-0 is running 14
Thread-1 is running 11
Thread-1 is running 12
Thread-0 is running 15
Thread-0 is running 16
Thread-0 is running 17
Thread-0 is running 18
Thread-1 is running 13
Thread-1 is running 14
Thread-0 is running 19
Thread-1 is running 15
Thread-1 is running 16
Thread-1 is running 17
Thread-1 is running 18
Thread-1 is running 19
join方法
/*
* join就是等待线程执行完毕后才能执行另一个线程
*/
public class JoinTest extends Thread {
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println(currentThread().getName()+" is running "+i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
JoinTest test=new JoinTest();
test.start();
try {
test.join(9000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("i am the main thread");
}
}
Thread-0 is running 0
Thread-0 is running 1
Thread-0 is running 2
Thread-0 is running 3
Thread-0 is running 4
Thread-0 is running 5
Thread-0 is running 6
Thread-0 is running 7
Thread-0 is running 8
i am the main thread
Thread-0 is running 9
wait方法
/**
*
* 模拟一个存钱取钱的过程:
* 要求必须先将钱存进去才能取出
*
*/
public class WaitTest {
//定义一个属性
private int i;
public static void main(String[] args) {
WaitTest tetsTest=new WaitTest();
PutInto into=new PutInto(tetsTest);
PutOut out=new PutOut(tetsTest);
into.start();
out.start();
}
public synchronized void putIntoMoney(){
//如果现在钱不是0,就要先取出来,等待其他的线程取钱,释放锁
if (i!=0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//值为0时需要加把钱放进去了
i++;
System.out.println("我把钱放进去了!");
//通知其他的可以进行取钱了
notify();
}
public synchronized void putOutMoney() {
//值为0需要先将钱放进去,等待其他线程放钱进去,先释放锁
if(i==0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//值为1时需要加把钱放取出了
i--;
System.out.println("我把钱取出来了!");
//通知其他的可以进行放钱了
notify();
}
}
//----放钱的线程--------
class PutInto extends Thread{
//模拟人
private WaitTest per;
public PutInto(WaitTest per){
this.per=per;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
per.putIntoMoney();
}
}
}
//------取钱的线程-------
class PutOut extends Thread{
//模拟人
private WaitTest per;
public PutOut(WaitTest per){
this.per=per;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
per.putOutMoney();
}
}
}
我把钱取出来了!
我把钱放进去了!
我把钱取出来了!
我把钱放进去了!
我把钱取出来了!
。。。。。。
总结下应用吧:
join方法:
等待一个线程完毕后才能执行下一个线程,会造成线程阻塞,比如你排队去取钱,只有上一个顾客被服务完,才能轮到你;
yield方法:
让正在执行的线程处于阻塞状态,可能线程调用此方法后立即获得执行的机会,也可能会让给优先级比它高的线程;
sleep方法:
和yield一样不会释放锁,使线程处于阻塞状态,sleep(time)超时会自动唤醒
wait方法
是在synchronized的同步代码块中使用
wait是Object的方法,在调用中会释放锁,如果多个对象对此对象进行操作就会获得此对象的锁;
可以使用notify唤醒在此同步监视器上的一个线程;
nitifyall唤醒在此同步监视器的所有线程;
6、线程池的启动策略
1、线程池刚创建时,里面没有一个线程,任务队列是作为参数传进来,不过,就算队列里面没有任务,线程池也不会马上执行他们;
2、当调用execute()方法添加一个任务时,线程池会做如下的判断:
图片来源与网络
下一章节我们讲解下线程的安全问题及线程池