黑马程序员——Java高新技术——多线程

点击打开链接 点击打开链接 点击打开链接 android培训、<a">点击打开链接 点击打开链接 java培训、期待与您交流!">点击打开链接 点击打开链接

 

多线程

进程:是一个正在进行的程序。

      每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。

线程:就是进程中的一个独立的控制单元。

      线程控制着进程的执行

一个进程中至少有一个线程

JVM 启动的时候会有一个java.exe

 

该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在main方法中。该线程称之为主线程。

 

扩展:其实更细节说明jvm,jvm启动不止一个线程,还有辅助垃圾回收机制的线程。

 

多线程存在的意义

多线程编程简单、效率高,易于资源共享。

 

如何在自定义代码中自定义线程?

1.1将类声明为Thread的子类,具体步骤如下:

class Demo extends Thread                                //①定义类继承Thread

{

      public void run()                                  //②复写Threadrun方法

      {

          for(int i=0;i<40;i++)

          {

          System.out.println("demo run");

          }

      }

}

  

class Test

{

   public static void main(String[] args)

   {

        Demo demo1=new Demo();

        demo1.start(); //③调用线程的start方法,该方法有2个作用,启动线程和调用run方法

        for(int i=0;i<40;i++)

        {

           System.out.println("main run-----");

        }

   }

}

1.定义类继承Thread

2.覆盖run方法

    会发现运行结果每一次都不同,这是因为多个线程都获取CPU的执行权,CPU执行到谁,谁就运行。明确一点讲,就是在某一时刻,只能有一个程序在运行(多核除外)。CPU在做着快速地切换,以达到看上去是同时运行的效果。我们可以形象的把多线程的运行行为看做是在互相抢夺CPU的执行权。这就是多线程的一个特性:随机性,谁抢到谁执行,至于执行多久,CPU说的算。

 

为什么覆盖run方法?

Thread类用于描述线程。

该类就定义了一个功能run,用于存储线程要执行的代码,该存储功能就是run方法,也就是说Thread类中的run方法,用于存储线程要运行的代码。

线程存在的几种状态:

 

Thread类

String   /  getName():返回该线程的名称,线程都有自己默认的名称,Thread-编号,从0开始。

Static Thread /  currentThread():返回当前正在执行的对象的名称。

void  setName(String):设置线程名称。构造函数也可以设置线程名称,不过要写构造函数继承父类。

static void  / sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠。

void       / start()   使该线程开始执行:java虚拟机调用该线程的run方法。

String     /  toString() 返回该线程的字符串表现形式,包括线程名称、优先级和线程组。

static void / yield()    暂停当前正在执行的线程对象,并执行其他线程。

void   /setPriority(int newpriority): 更改线程的优先级,提供了三个常量:MAX_PRIORITYMIN_PRIORITYNORM_PRIORITY

void   /  setDeamon(boolean on):将该线程标记为守护线程或用户线程。

void   /  join():等待该线程终止,此外还有joinlong millis)、jion(long millis,int nanos),设置等待该线程结束的时间。

boolean  /  isInterrupted()  测试该线程是否已经中断

boolean  /  isDemo()    测试该线程是否为守护线程

boolean  /  isAlive()     测试该线程是否处于活动状态

static boolean / interrupted()  测试当前线程是否已经中断

void      /  interrupt()    中断线程

 

创建线程的第二种方式

步骤:

1.定义类实现Runnable接口

2.覆盖Runnable接口中的run方法。

3.通过Thread类建立线程对象

4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数

    自定义的run方法所属的对象是Runnable接口的子类对象,要让线程去执行指定对象的run方法,就必须明确run方法所属对象

5.调用Threadstart方法开启线程并调用Runnable接口子类的run方法。

 

class Demo1 implements Runnable  //第一步,实现Runnable接口
{
	public void run()             //第二步,覆盖run方法
	{
        for(int i=0;i<40;i++)
		{
            System.out.println(Thread.currentThread().getName()+"::"+i);
		}
	}
}

class ThreadDemo 
{
	public static void main(String[] args) 
	{
		Demo1  d=new Demo1();    //第三步,建立Runnable的子类对象
		Thread th=new Thread(d); //第四步.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
		th.start();               //第五步,调用Thread的start方法开启线程并调用Runnable接口子类的run方法。
	}
}

 

实现方式和继承方式的区别

实现方式的好处:避免单继承的局限性

在定义线程时,建议使用实现方式

继承Thread:线程代码存放在Thread子类的run方法

实现Runnable:线程代码存放在接口的子类run方法中。

 

多线程的运行出现了安全问题

问题的原因:

    当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分还没执行完,另一个线程参与进来执行,导致共享数据的错误。

解决办法:对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程都不参与执行。

Java对线程安全问题提供了专业的解决方式,就是同步代码块

  synchronized(对象)

  {

     需要同步的代码块;

  }

对象如同锁,持有锁的线程可在同步中执行。

没有锁的线程即使获得CPU的执行权也进不去,因为没有获取锁。

 

例:Test9

class Test9 
{
	public static void main(String[] args)
	{
	   Demo d=new Demo();
	   Thread t1=new Thread(d);
	   Thread t2=new Thread(d);
	   t1.start();
	   t2.start();
	  }
}

class Demo implements Runnable
{
	public int s=100;
	public void run()
	{
		while(s>=0)
		{
			synchronized(Demo.class)
			{
				if(s>=0)
		        {
					System.out.println(Thread.currentThread().getName()+"售出了第"+s+"张票");
                    s=s-1;
				}
			}
		}	
	}
}

同步的前提:

1.必须要有两个或两个以上的线程

2.必须是多个线程使用同一个锁

3.必须保证同步中只有一个线程在运行。

同步的利与弊

好处:解决多线程的安全问题

弊端:多线程需要判断锁,较为耗费资源。

 

同步函数

     函数需要被对象调用,那么函数都一个所属对象引用,就是this,所以同步函数的锁是this。如果同步函数被static修饰后,所使用的锁不是this,因为静态方法中不可以定义this,静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象:类名.class,该对象的类型是class,静态的同步方法,使用的锁是该方法所在类的字节码文件对象:类名.class

 

死锁:同步嵌套,而锁不同。Test10

class Test10   //死锁实例
{
    public static void main(String[] args)
	{
	    DeadLock d1=new DeadLock(true);
	    DeadLock d2=new DeadLock(false);
		Thread t1=new Thread(d1);
		t1.start();
		Thread t2=new Thread(d2);
		t2.start();
	}
}

class DeadLock implements Runnable
{
    private boolean flag;
	DeadLock(boolean flag)
	{
		this.flag=flag;
	}
	public void run()
	{
		if(flag)
		{
			synchronized(Mylock.obj1)
			{
				System.out.println("if.....this");
				synchronized(Mylock.obj2)
				{
					System.out.println("if.....flag");
				}
			}
		}else
		{
            synchronized(Mylock.obj2)
			{
				System.out.println("else.....flag");
				synchronized(Mylock.obj1)
				{
					System.out.println("else.....this");
				}
			}
		}
	}
}

class Mylock
{
	static Object obj1=new Object();
	static Object obj2=new Object();
}

线程间通讯:其实就是多个线程在操作同一个资源。但是操作的动作不同。 

等待唤醒机制,例:Test11

class Test11 
{
	public static void main(String[] args) 
	{
		Res s=new Res();
		new Thread(new Input(s)).start();
		new Thread(new Output(s)).start();
        new Thread(new Input(s)).start();
		new Thread(new Output(s)).start();
	}
}

class Res  //资源类
{
	private String name;
	private String sex;
    boolean flag=false; //定义一个标记

	//同步函数,输入资源
	public synchronized void set(String name,String sex)
	{
		if(this.flag)//当标记为真时,让此线程等待
			 try{wait();}catch(Exception e){}

	    //当标记为假或者,线程被唤醒,,就会执行以下代码块
		this.name=name;
		this.sex=sex;
        System.out.println("输入了……"+name+"::"+sex);
		//把标记变为真,并唤醒线程池中等待的线程
		this.flag=true;
		this.notify();
	}

	//同步函数,输出资源
	public synchronized void print()
	{
		//当标记为假时,让此线程等待
		if(!this.flag)
			 try{wait();}catch(Exception e){}

		//当标记为真,或者线程被唤醒,就会执行输出语句
	   System.out.println(this.name+"…………"+this.sex);

	   //输出完,让标记变为false,并唤醒输入线程
	   this.flag=false;
	   this.notify();  //通常唤醒线程池中第一个等待的
	}
}

//输入类
class Input implements Runnable
{
	private Res s;
	Input(Res s)
	{
		this.s=s;
	}
	public void run()
	{
		int x=0;
		while(true)
		{ 
			   if(x==0)
			    {
			      s.set("make","nan");
		        }else
			    {
				    s.set("丽丽","女");
			    }
			    x=(x+1)%2;
		}
	}
}

//输出类
class Output implements Runnable
{

	private Res s;
	Output(Res s)
	{
			this.s=s;
	}
	public void run()
	{
		while(true)
		{
			  s.print();
		}
	}
}


 

wait()

notify()

notifyAll() 

   这三个定义在都使用在同步中,因为要对持有监视器(锁)的线程操作,所以要使用在同步中,因为只有同步才具有锁。

 

为什么这些操作线程的方法要定义在Object类中呢?

   因为这些方法在操纵同步线程时,都必须要标识它们所操作线程中的锁。只有同一个锁上的被等待线程,可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒。

也就是说等待和唤醒必须是同一个锁,而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。

 

   例:Test12

class Test12   //生产消费实例
{
	public static void main(String[] args) 
	{
		Res s=new Res();
		Thread t1=new Thread(new Produce(s)).start();
        Thread t2=new Thread(new Produce(s)).start();
		Thread t3=new Thread(new Consum(s)).start();
        Thread t4=new Thread(new Consum(s)).start();
	}
}

//资源类
class Res
{
	private String name;
	private int count=0;
	private boolean flag=false;
	public synchronized void set(String name)
	{
		if(flag)    //t1、t2判断完标记后分别陷入在此等待,而后t1被唤醒,生产了一个商品,
		            //而后可能把t2给唤醒,t2会直接执行wait后的代码块,又生产一个商品。
					//这显然不符合要求,生产了两个商品,而有一个没被卖掉,
					//究其原因,是线程被唤醒后没再次判断标记引起的,为了解决这个问题,我们只要把if变成while,循环判断即可。
			try{this.wait();}catch(Exception e){}
		this.name=name+count;
		System.out.println("生产了"+this.name);
		count++;
		flag=true;
		this.notify();
	}
	public synchronized void out()
	{
		while(!flag)
		    try{this.wait();}catch(Exception e){}
		System.out.println("售出了"+this.name+".....");
		flag=false;
		this.notify();   //当生产和销售方法里的if都变成whlie后,会产生一个新的问题,就是如果t1、t2、t3三个线程相继陷入等待,
		                  //t4运行完,自个陷入等待,t1被唤醒,执行完,后唤醒的是线程池中第一个等待的线程t2,而t2一运行就会判断
						  //标记,再次陷入等待中,这样四个线程都在等待,程序就被挂起了。所以这里应该用notifyAll().
	}
}
class Produce implements Runnable
{
    private Res s;
	Produce(Res s)
	{
		this.s=s;
	}
	public void run()
	{
        while(true)
		{
			s.set("苹果");
		}
	}
}

class Consum implements Runnable
{
    private Res s;
	Consum(Res s)
	{
		this.s=s;
	}
	public void run()
	{
        while(true)
		{
			s.out();
		}
	}
}


    在以上的例子中,当生产或消费一方的线程运行后,我们希望唤醒的是另一方的线程,而notifyAll连本方线程一起唤醒,如何解决这个问题呢?

JDK1.5中提供了线程升级解决方案

l 将同步synchronized替换成实现lock操作

l 将object中的waitnotifynotifyAll替换了condition对象

l 该类可以对lock锁进行获取

l 该实例中实现了本方只唤醒对方的操作。

Test13

import java.util.concurrent.locks.*;
class Test13   //生产消费实例升级版
{
	public static void main(String[] args) 
	{
		Res s=new Res();
		new Thread(new Produce(s)).start();
		new Thread(new Consum(s)).start();
        new Thread(new Produce(s)).start();
		new Thread(new Consum(s)).start();
	}
}

class Res
{
	private String name;
	private int count=0;
	private boolean flag=false;
	private Lock lock=new ReentrantLock();  //同步换成锁
	private Condition con_pro=lock.newCondition();
	private Condition con_con=lock.newCondition();
	public  void set(String name) throws InterruptedException
	{
		lock.lock();
		try
		{
			while(flag)
			     con_pro.await();  //wait替换成Condition的con_pro对象的await方法
		     this.name=name+count;
		     System.out.println("生产了"+this.name);
		     count++;
		     flag=true;
             con_con.signal();    //notify替换成Condition的con_con对象的signal方法,只会唤醒对方等待线程
		}
		finally
		{
            lock.unlock();
		}
		
	}
	public  void out() throws InterruptedException
	{
		lock.lock();
	    try
	    {
			while(!flag)
		         con_con.await();
		     System.out.println("售出了"+this.name+".....");
		     flag=false;
		     con_pro.signal();   //只会唤醒对方等待的线程
	    }
	    finally
	    {
			lock.unlock();
	    }
		
	}
}
class Produce implements Runnable
{
    private Res s;
	Produce(Res s)
	{
		this.s=s;
	}
	public void run()
	{
        while(true)
		{
			try{s.set("苹果");}catch(InterruptedException e){}
		}
	}
}

class Consum implements Runnable
{
    private Res s;
	Consum(Res s)
	{
		this.s=s;
	}
	public void run()
	{
        while(true)
		{
			try{s.out();}catch(InterruptedException e){}
		}
	}
}


 

Lock接口

   在java.util.concurrent.locks包中,Lock实现提供了比使用synchronized方法和语句可获得更广泛的锁的操作。此实现允许更灵活的结构,可以具有很大差别的属性,可以支持多个相关的Condition 对象。

已知实现类ReentranLockReentrantReadWriteLock.ReadLockReentrantReadWriteLock.WriteLock,

 

方法摘要

 void  / lock():获取锁。

  void  /lockInterruptibly():如果当前线程未被中断则获取锁。

 Condition / newCondition():返回绑定到此Lock实例的新Condition实例。

 boolean  / tryLock():仅在调用时锁为空闲状态则获取锁

 boolean   / tryLock(Long time,TimeUnit Unit):如果锁在给定的等待时间内空闲,并且当前线程未被中断,则获取锁

 void unlock():释放锁。

 

在同一个包中,Condition 将 Object 监视器方法(waitnotify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

方法摘要:

void  / await():造成当前线程等待

Boolean  / await(long time,TimeUnit unit):在达到指定等待时间之前等待。uint的参数:TimeUnit.DAYS  HOURS  MICROSECONDS  MILLISECONDS  MINUTES、NANOSECONDS、SECONDS.

long await()Nanos(long nanosTimeout):造成当前线程在达到指定等待时间之前等待。

void   / awaitUninterruptibly():造成当前线程在接收到信号之前一直处于等待状态。

boolean /awaitUnitil(Date deadline):造成当前线程在达到指定最后期限之前一直处于等待状态

void / signal()唤醒一个等待线程

void/signalAll()唤醒所有等待线程。

 

停止线程

1.定义循环结束标记

   因为线程运行代码一般都是循环,只要控制了循环即可

2.使用interrupt(中断)方法

   该方法是结束线程的冻结状态,使线程回到运行状态中来。

   注:stop方法已经过时不再使用。

 

停止线程,stop方法已经过时,只有run方法运行结束。开始多线程运行,运行代码通常是循环结构。只要控制循环,就可以让run方法结束,也就是线程结束。

 

特殊情况:

当线程处于冻结状态,就不会读取到标记,那么线程就不会结束。

Interrupt()将处于冻结状态的线程强制让线程恢复到运行状态。

当没有指定方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除,强制让线程恢复到运行状态来,这样就可以操作标记让线程结束。

 

setDeamon(boolean b) 将该线程标记为守护线程或用户线程,当所有线程都为守护线程时,JVM退出。该方法必须在启动线程前调用。

 

join():等待该线程终止。当A线程执行到了B线程的Join方法时,A线程就会等待B线程执行完,A线程才执行。Join可以用来临时加入线程执行。

 

setPriority(int ~):线程优先级,三个常量:Thread.MAX_PRIORITYThread.MIN_PRIORITYThread.NORM_PRIORITY.

 

static void / yield():暂停当前正在执行的线程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
电子图书资源服务系统是一款基于 Java Swing 的 C-S 应用,旨在提供电子图书资源一站式服务,可从系统提供的图书资源中直接检索资源并进行下载。.zip优质项目,资源经过严格测试可直接运行成功且功能正常的情况才上传,可轻松copy复刻,拿到资料包后可轻松复现出一样的项目。 本人系统开发经验充足,有任何使用问题欢迎随时与我联系,我会及时为你解惑,提供帮助。 【资源内容】:包含完整源码+工程文件+说明(若有),项目具体内容可查看下方的资源详情。 【附带帮助】: 若还需要相关开发工具、学习资料等,我会提供帮助,提供资料,鼓励学习进步。 【本人专注计算机领域】: 有任何使用问题欢迎随时与我联系,我会及时解答,第一时间为你提供帮助,CSDN博客端可私信,为你解惑,欢迎交流。 【适合场景】: 相关项目设计中,皆可应用在项目开发、毕业设计、课程设计、期末/期中/大作业、工程实训、大创等学科竞赛比赛、初期项目立项、学习/练手等方面中 可借鉴此优质项目实现复刻,也可以基于此项目进行扩展来开发出更多功能 【无积分此资源可联系获取】 # 注意 1. 本资源仅用于开源学习和技术交流。不可商用等,一切后果由使用者承担。 2. 部分字体以及插图等来自网络,若是侵权请联系删除。积分/付费仅作为资源整理辛苦费用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值