进程与多线程
1. 进程与线程的概念
1.进程
操作系统中程序的一次执行过程,是操作系统资源分配的最小单元。操作系统为每个进程分配独立的内存
进程是独立的
2.线程
线程在进程中的一个子任务,是操作系统任务分配的最小单元,在一个进程中的所有线程共享线程资源。
每个子任务对应一个线程
3.进程与线程关系
1.进程是操作系统资源分配的最小单元,线程是操作系统任务分配的最小单元.
2.进程的启动与销毁开销较大,与此相比,启动与销毁一个线程开销小的多.
3.进程间通信远比线程间通信复杂的多。
-
高并发:在同一时刻,并发访问的线程数量非常高(12306、淘宝)
加载:服务器内存不够 等待其他线程退出
DDos :高并发漏洞
-
高可用:若干台机器的损坏不能影响其他服务器
-
分布式:一个程序同时部署在多台机器上
2.java中多线程实现
java.long.Thread 此类是java中多线程的核心类
创建线程方法:
a.自定义类继承Thread类,而后覆写run方法(run()放线程任务),然后调用start
package ThreadTest.MyThread;
/**
* @Name:
* @Author:ZYJ
* @Date:2019-05-28-22:33
* @Description:
*/
public class TestDemo {
public static void main(String[] args) {
new myThread().start();
new myThread().start();
new myThread().start();
}
}
class myThread extends Thread{
private int ticket =10;
public void run(){
while(this.ticket>0){
System.out.println("剩余票数"+this.ticket--);
}
}
}
启动线程:调用 start(Thread类的方法)
start() 线程的启动一律使用此方法
一个线程start()方法只能被调用一次,执行多次会抛出IllegalThreadStateExceptiom
异常
b.实现Runnable接口然后覆写 run方法
Thread可以接收一个Runnable的对象(通过构造方法)
因为Thread类本身也实现了Runnable接口,与用户自定义的线程类共同组成代理设计模式
其中Thread类实现辅助操作,包括线程的资源调度等任务;自定义线程类完成真实任务。
package ThreadClass;
/**
* @Name:
* @Author:ZYJ
* @Date:2019-05-21-19:30
* @Description:
*/
public class Class {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread1= new Thread(myThread);
Thread thread2= new Thread(myThread);
Thread thread3= new Thread(myThread);
thread1.start();
thread2.start();
thread3.start();
}
}
class MyThread implements Runnable {
private int ticket = 20;
@Override
public void run() {
while (ticket > 0) {
System.out.println("当前线程为" + Thread.currentThread().getName() + "还剩" + ticket-- + "票");
}
}
}
> public interface RunnableRunnable接口应由任何类实现,其实例将由线程执行。
> 该类必须定义一个无参数的方法,称为run 。 该接口旨在为希望在活动时执行代码的对象提供一个通用协议。 例如,
> Runnable由Thread类Thread 。 活跃的只是意味着一个线程已经启动,还没有被停止。
>
> 另外, Runnable提供了一个类被激活而不是Thread Thread类化的Thread 。
> 一个实现类Runnable可以在不继承运行Thread实例化一个Thread实例,并在传递本身作为目标。 在大多数情况下,
> Runnable接口应使用,如果你只打算重写run()方法并没有其他Thread方法。
> 这是重要的,因为类不应该被子类化,除非程序员打算修改或增强类的基本行为。
继承Thread类与实现Runnable接口的区别
1.继承Thread类有单继承局限,相对而言实现Runnable接口更加灵活。并且Thread类本身也实现了Runnable接口,辅助真实线程类。
2.实现Runnable接口可以更好的实现程序共享的概念(Thread类也可以,稍微麻烦点)
c:实现Callable接口,而后覆写call 方法
V call() throws Exception; 线程执行任务后有返回值
Future接口:接收Callable接口的返回值
V get():接收Callable方法
产生callable对象,
产生FutureTask
对象,可以接收callable对象
将FutureTask
作为参数传入 Thread中
1)定义一个真实的线程类实现Callable接口,覆写call()方法,真实线程类实例化
2)有一个Thread类的实例化对象类 start()启动线程
3)有一个FutureTask
对象
Thread没有构造方法可以传入Callable对象,所以涉及Future和Runnable的共同间接子类FutureTask
对象
它调用Future
的 get
()方法可以接收Callable的返回值。然后把FutureTask
对象(Runnable子类)传给Thread对 象
package ThreadClass;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Name:
* @Author:ZYJ
* @Date:2019-05-21-20:06
* @Description:
*/
public class CallThrad {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> task = new FutureTask<>(new MycallThread());
new Thread(task).start();
new Thread(task).start();
System.out.println(task.get());
}
}
class MycallThread implements Callable<String>{
private int ticket =20;
public String call(){
while (ticket>0){
System.out.println("剩余票数:"+ticket--);
}
return "没了!";
}
}
3. java中常用线程操作方法
取得当前JVM中正在执行的线程对象
public static native Thread currentThread()
###3.1 线程名称的命名与取得
3.1.1 线程命名
public Thread(Runable target,String name)//构造方法
public final synchronized void setName(String name)
3.1.2取得名称
String getName()
Thread.currentThread().getName();
3.2 线程休眠 sleep()
3.2.1定义:
public static native void sleep(long mills) throws InterruptedException
让线程暂缓执行,等到了预计时间之后再恢复执行 单位为ms
sleep方法会让当前线程立即交出CPU,但不会释放对象锁
3.2.2 代码实现
package ThreadTest.SleepTest;
/**
* @Name:
* @Author:ZYJ
* @Date:2019-05-28-22:57
* @Description:
*/
public class sleepDemo {
public static void main(String[] args) {
ThreadDemo threadDemo =new ThreadDemo();
new Thread(threadDemo).start();
new Thread(threadDemo).start();
new Thread(threadDemo).start();
}
}
class ThreadDemo implements Runnable{
@Override
public void run() {
for(int i=0;i<1000;i++){
try{
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程"+Thread.currentThread().getName()+",i="+i);
}
}
}
3.3 线程让步 yield()
暂停当前正在执行的线程对象,并执行其他线程(只能让拥有相同优先级的线程获取CPU)。
调用此方法之后会让当前线程交出CPU,让CPU去执行其他线程,不会释放锁。
注意!!!:yield
不能控制具体的交出CPU的时间,yield方法只能让拥有相同优先级的线程有获得CPU执行时间的机会
调用yield()
方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间。
3.3.1 定义
public native static yield()//同样不会释放锁
3.3.2 代码实现
package ThreadTest.yieldTest;
/**
* @Name:
* @Author:ZYJ
* @Date:2019-05-28-23:08
* @Description:
*/
public class yieldDemo {
public static void main(String[] args) {
mynewThread mynewThread1 =new mynewThread();
new Thread(mynewThread1).start();
new Thread(mynewThread1).start();
new Thread(mynewThread1).start();
}
}
class mynewThread implements Runnable{
@Override
public void run() {
for(int i=0;i<3;i++){
Thread.yield();
System.out.println("当前线程为"+Thread.currentThread().getName()+",i="+i);
}
}
}
3.4 线程等待 join()
3.4.1 定义
等待线程终止 如果主线程中调用该方法时就会让主线程休眠,让调用该方法的线程run方法先执行,完毕之后再开始执行主线程
public final void ioin() throws InterruptedException
若一个线程1需要等到另一个线程2执行完毕后再恢复执行,可以在线程1中调用线程2的join()
在哪个线程中调用,哪个线程阻塞,等待线程执行完毕再恢复执行
从运行态到阻塞态,会释放锁
package ThreadTest.joinTest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @Name:join()练习
* @Author:ZYJ
* @Date:2019-05-28-23:32
* @Description:
*/
public class joinDemo {
public static void main(String[] args) throws InterruptedException {
myThread mythread = new myThread();
Thread thread = new Thread(mythread,"子线程A");
thread.start();
System.out.println(Thread.currentThread().getName());
//主线程调用线程等待
thread.join();
System.out.println("代码结束");
}
public static void printTime(){
Date date = new Date();
DateFormat format = new SimpleDateFormat("yyyy-mm-dd hh:mm:ss");
String time = format.format(date);
System.out.println(time);
}
}
class myThread implements Runnable{
@Override
public void run() {
try{
System.out.println("主线程睡眠前时间");
joinDemo.printTime();
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName());
System.out.println("睡眠时间结束");
joinDemo.printTime();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.5 线程停止
3.5.1 线程停止的三种方法:
1)设置标志位;(无法处理线程阻塞时停止问题)
线程中设置boolean
类型的标志位,(flag,利用set方法在线程外,休眠一段时间后关闭线程);
package ThreadTest.FlagTest;
/**
* @Name:线程停止
* @Author:ZYJ
* @Date:2019-05-29-10:22
* @Description:
*/
public class FlagTestDemo {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
Thread thread1= new Thread(myThread,"子线程A");
thread1.start();
Thread.sleep(2000);
myThread.setFlag(false);
System.out.println("program over");
}
}
class MyThread implements Runnable{
//定义标记类
private boolean flag =true;
@Override
public void run() {
int i=1;
while(flag){
try {
Thread.sleep(1000);
System.out.println("第"+i+"次执行,线程名称为"+Thread.currentThread().getName());
i++;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//设置标记
public void setFlag(boolean flag){
this.flag=flag;
}
}
**2)**调用Thread
提供的stoop()
方法,强行关闭线程;(无论是否执行完,都关闭线程,后面的程序不再执行。本方法不推荐使用,因为会产生不完整数据);
stop会解除由线程获取的所有锁定,当在一个线程对象上调用stop()方法时,这个线程对象所运行的线程就会理解停止,如果这是线程中正在进行赋值操作,这样会产生不完整的残存数据。
**3)**通过调用Thread提供的interrupt()
方法
package ThreadTest.interruptTest;
/**
* @Name:interrupt()方法练习
* @Author:ZYJ
* @Date:2019-05-29-10:55
* @Description:
*/
public class InterruptDemo {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
Thread thread1= new Thread(myThread,"子线程A");
thread1.start();
Thread.sleep(3000);
//掉用interrupt()设置中断标志
thread1.interrupt();
System.out.println("代码结束!");
}
}
class MyThread implements Runnable{
private boolean flag =true;
@Override
public void run() {
int i=1;
while (flag){
try {
Thread.sleep(1000);//阻塞之后
//currentThread()取得当前执行的线程对象
boolean bool = Thread.currentThread().isInterrupted();
//清除中断,抛出异常
if(bool){
System.out.println("非阻塞情况下执行该操作。。。线程状态"+bool);
break;
}
System.out.println("第"+i+"次执行,线程名称为:"+Thread.currentThread().getName());
i++;
} catch (InterruptedException e) {
System.out.println("退出了");
boolean bool = Thread.currentThread().isInterrupted();
System.out.println(bool);
return;
}
}
}
public void setFlag(boolean flag){
this.flag=flag;
}
}
Thread
中提供的isinterrupt()
可以检测当前线程状态是否处于中断状态。调用interrupt()
方法会将状态置为true
; 这个方法只会给线程设置一个为true的中断标志(中断标志只是一个boolean
类型的变量),而设置之后,则根据线程当前的状态进行不同的后续操作。如果线程处于非阻塞状态,那么仅仅是线程的中断标志被修改为true状态而已;如果线程处于阻塞状态,那么在将中断标志置为true之后,有以下操作
a,如果是wait
、sleep
、join
三个方法引起的阻塞,那么会将线程的中断标志置为false,并抛出一个InterrputedEcxception
;
b,如果在中断时,线程正处于非阻塞状态,则将中断标志修改为true,一旦进入阻塞状态,按照阻塞状态情况来处理。