线程与进程
进程(Process):操作系统中能够同时运行的多个软件(QQ、浏览器、Word、WPS),实际上在底层上是“分时”系统,如何分时?由操作系统决定
线程(Thread):一个应用程序中,能够同时运行的多个任务,比如:在线播放(一边下载、一边播放),实际上在底层上是“分时”系统,如何分时?由操作系统决定
很多软件都支持线程功能:QQ(用户一边接受信息,一边发送信息);抖音(一边下载,一边播放),针对Java语言,我们讲解线程;如果是讲解进程,在操作系统课中
线程
例子:对于某软件,需要一边下载,一边播放,怎么实现?
情景一:不用线程怎么做?
class Downloader{
public void download()
{
for(int i=1;i<=10;i++)
{
System.out.println("下载进度:"+i*10+"%");
}
}
}
class Player{
public void Play()
{
for(int i=1;i<=10;i++)
{
System.out.println("播放进度:"+i*10+"%");
}
}
}
public class TreadTest {
public static void main(String[] args) {
Downloader d=new Downloader();
Player p=new Player();
d.download();
p.Play();
}
}
实际考虑:
class Downloader{
public void download()
{
for(int i=1;i<=10;i++)
{
try{Thread.sleep(1000);}catch(Exception ex){}
System.out.println("下载进度:"+i*10+"%");
}
}
}
class Player{
public void Play()
{
for(int i=1;i<=10;i++)
{
try{Thread.sleep(1000);}catch(Exception ex){}
System.out.println("播放进度:"+i*10+"%");
}
}
}
public class TreadTest {
public static void main(String[] args) {
Downloader d=new Downloader();
Player p=new Player();
d.download();
p.Play();
}
}
情景2:如何用线程解决此问题?
实现线程的两种办法:
1. 让需要实现线程的类,继承Java.lang.Thread类;讲需要实现线程的代码,放在从Thread类中重写的run函数里面;开启线程,用线程对象的start方法
问题:用了多线程,多个任务完成的比以前快,是不是说明多线程让CPU运行的更快了呢?不是。只是让CPU的利用率提高了!
class Downloader extends Thread{//第一步:继承Thread
public void run()//第二步:重写run函数
{
for(int i=1;i<=10;i++)
{
try{Thread.sleep(1000);}catch(Exception ex){}
System.out.println("下载进度:"+i*10+"%");
}
}
}
class Player extends Thread{
public void run()
{
for(int i=1;i<=10;i++)
{
try{Thread.sleep(1000);}catch(Exception ex){}
System.out.println("播放进度:"+i*10+"%");
}
}
}
public class TreadTest {
public static void main(String[] args) {
Downloader d=new Downloader();
Player p=new Player();
d.start(); p.start();//用start方法
}
}
2.让需要实现线程的类,实现Java.lang.Runnable接口;讲需要实现线程的代码,放在.Runnable接口重写的run函数里面;实例化线程,将类的对象传入线程呢个的构造函数,再调用线程的start方法
class Downloader implements Runnable{//第一步:接口Runnable
public void run()//第二步:重写run函数
{
for(int i=1;i<=10;i++)
{
try{Thread.sleep(1000);}catch(Exception ex){}
System.out.println("下载进度:"+i*10+"%");
}
}
}
class Player implements Runnable{
public void run()
{
for(int i=1;i<=10;i++)
{
try{Thread.sleep(1000);}catch(Exception ex){}
System.out.println("播放进度:"+i*10+"%");
}
}
}
public class TreadTest {
public static void main(String[] args) {
Downloader d=new Downloader();
Thread th1=new Thread(d);
Player p=new Player();
Thread th2=new Thread(p);
th1.start(); th2.start();//第三步:实例化线程,将类的对象传入线程的构造函数,再调用线程的start方法
}
}
额外内容:
线程控制(线程开启、暂停、继续、结束)
线程开启:start
线程暂停:suspend
线程继续:resume
线程结束:run函数运行完毕
例5:下载文件,下载到30%,暂停下载5秒,5秒后继续下载
class Downloader extends Thread{
public void run() {
for(int i=1;i<=10;i++){
try{ Thread.sleep(1000); } catch(Exception ex){}
System.out.println("下载进度:"+i*10+"%");
}
}
}
class ThreadTest{
public static void main (String[] args) throws Exception {
Downloader d = new Downloader();
d.start();
Thread.sleep(3200);
d.suspend();
Thread.sleep(5000);
d.resume();
}
}
注意:suspend、resume是不建议使用的函数,有死锁倾向
这两个函数,特别是suspend,在暂停时不会释放线程中的资源,导致资源被该线程长期持有,别人不能使用。可能造成循环等待。【死锁的概念,详见操作系统课程】所以不建议使用。
怎么办?线程暂停,就让该线程结束(run函数运行完毕);线程继续,新开启一个线程(start)
改进:
class Downloader extends Thread{
boolean RUN = true;
static int i = 1;
public void run() {
for(;i<=10&&RUN;i++){
try{ Thread.sleep(1000); } catch(Exception ex){}
System.out.println("播放进度:"+i*10+"%");
}
}
}
class ThreadTest{
public static void main (String[] args) throws Exception {
Downloader d = new Downloader();
d.start();
Thread.sleep(3000);
d.RUN = false;
Thread.sleep(5000);
d = new Downloader();
d.start();
}
}
线程同步:主要出现在“多个线程访问同一内存资源的场合”
例子:下载文件时,如果同时开多个线程下载文件,可以提高下载速度
举例:两个线程,下载文件。
解决同步问题:用synchronized将原子代码包含起来,保证其要么全部运行,要么全部不运行,别的线程在此之间,无法抢占CPU
class Downloader implements Runnable{
static int i = 1;
public void run() {
while(true){
synchronized(this){
if(i>10) { break; }
try{ Thread.sleep(1000); } catch(Exception ex){}
System.out.println("下载进度:"+i*10+"%");
i++;
}
}
}
}
class ThreadTest{
public static void main (String[] args) throws Exception {
Downloader d = new Downloader();
new Thread(d).start(); new Thread(d).start();
}
}
这种方式,实际上是将线程功能进行了退化,“几乎又变成串行了!”
实际过程中,如何解决此问题呢?方法是:人为进行资源划分。