黑马程序员--------------多线程

----------------Android培训java培训、期待与您交流---------------

多线程

1.线程的概念

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

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

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

    线程在控制着进程的执行

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

    Java VM  启动的时候会有一个进程java.exe.

    该进程中至少一个线程负责java程序的执行。

而且这个线程运行的代码存在于main方法中。该线程称之为主线程。

其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。

2.自定义线程

    通过对api的查找,java已经提供了对线程这类事物的描述。就是Thread类。

(1)创建线程的第一种方式:继承Thread类。

步骤:

  1、定义类继承Thread。

  2、复写Thread类中的run方法。

    目的:将自定义代码存储在run方法。让线程运行。

  3、调用线程的start方法,

    该方法两个作用:启动线程,调用run方法。

覆盖run方法的原因:

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

也就是说Thread类中的run方法,用于存储线程要运行的代码。

多线程运行结果每一次都不同。

    因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。

明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)

cpu在做着快速的切换,以达到看上去是同时运行的效果。

我们可以形象把多线程的运行形容为在互相抢夺cpu的执行权。

这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。

多线程运行状态图


通过下例两个线程的交替运行理解继承Thread类的多线程:

class Test extends Thread
{
	Test(String name)
	{
		super(name);
	}
	public void run()
	{
		for(int x=0; x<60; x++)
		{
			System.out.println((Thread.currentThread()==this)+"..."+this.getName()+" run..."+x);
		}
	}

}
class ThreadTest 
{
	public static void main(String[] args) 
	{
		Test t1 = new Test("one---");
		Test t2 = new Test("two+++");
		t1.start();//开启线程
		t2.start();//开启线程
		for(int x=0; x<60; x++)
		{
			System.out.println("main....."+x);
		}
	}
}
    static Thread currentThread():获取当前线程对象。

    getName(): 获取线程名称。

(2)创建线程的第二种方式:实现Runable接口

    通过设计一个买票程序理解实现Runable接口多线程

    需求分析:可以多个窗口同时卖票,即存在多线程

步骤:

1、定义类实现Runnable接口

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

    将线程要运行的代码存放在该run方法中。

3、通过Thread类建立线程对象。

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

为什么要将Runnable接口的子类对象传递给Thread的构造函数。

因为,自定义的run方法所属的对象是Runnable接口的子类对象。

所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。

5、调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。

实现方式和继承方式有什么区别呢?

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

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

两种方式区别:

    继承Thread:线程代码存放Thread子类run方法中。

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

3.同步

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

问题的原因:

    当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,

    另一个线程参与进来执行。导致共享数据的错误。

解决办法:

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

Java对于多线程的安全问题提供了专业的解决方式。

就是同步代码块。

synchronized(对象)

{
        需要被同步的代码
}

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

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

同步的前提:

    1、必须要有两个或者两个以上的线程。

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

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

多线程安全问题的解决方案的利弊:

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

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

代码如下:

class Ticket implements Runnable
{
	private  int tick = 1000;
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			synchronized(obj)
			{
				if(tick>0)
				{
					System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);
				}
			}
		}
	}
}


class  TicketDemo
{
	public static void main(String[] args) 
	{

		Ticket t = new Ticket();

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		Thread t3 = new Thread(t);
		Thread t4 = new Thread(t);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

    函数需要被对象调用。那么函数都有一个所属对象引用。就是this。

    所以同步函数使用的锁是this。

如果同步函数被静态修饰后,使用的锁是什么呢?

    通过验证,发现不在是this。因为静态方法中也不可以定义this。

静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

    类名.class  该对象的类型是Class

    静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class

class Ticket implements Runnable
{
	private static  int tick = 100;
	boolean flag = true;
	public  void run()
	{
		if(flag)
		{
			while(true)
			{
				synchronized(Ticket.class)
				{
					if(tick>0)
					{
						try{Thread.sleep(10);}catch(Exception e){}
						System.out.println(Thread.currentThread().getName()+"....code : "+ tick--);
					}
				}
			}
		}
		else
			while(true)
				show();
	}
	public static synchronized void show()
	{
		if(tick>0)
		{
			try{Thread.sleep(10);}catch(Exception e){}
			System.out.println(Thread.currentThread().getName()+"....show.... : "+ tick--);
		}
	}
}
class  StaticMethodDemo
{
	public static void main(String[] args) 
	{

		Ticket t = new Ticket();

		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();
		try{Thread.sleep(10);}catch(Exception e){}
		t.flag = false;
		t2.start();
	}
}

同步中嵌套同步容易出现死锁,需要注意避免死锁。

通过以下代码分析多线程代码执行:

class MyThread extends Thread{
	public void run(){
		try {
			Thread.currentThread().sleep(3000);
		} catch (InterruptedException e) {
		}
		System.out.println("MyThread running");
	}
}
public class ThreadTest{
	public static void main(String argv[]) {
		MyThread t = new MyThread();
		t.run();
		t.start();
		System.out.println("Thread Test");
	  }
}
代码分析过程:

MyThread t = new MyThread();  创建了一个线程。

    t.run();

    调用MyThread对象的run方法。

    这是只有一个线程在运行就是主线程。

    当主线程执行到了run方法中的sleep(3000);时。

    这是主线程处于冻结状态。程序并没有任何执行。

    当3秒过后,主线程打印了  MyThread running。 run方法执行结束。

t.start();

    开启了t线程。

    有两种可能情况。

    第一种,主线程在只执行了t.start()后,还具有执行权,继续往下执行,

    打印了Thread Test。主线程结束。

    t线程获取执行权,调用自己的run方法。然后执行的sleep(3000);冻结3秒。

   3秒后,打印MyThread running t线程结束,整个程序结束。

第二种情况:

    主线程执行到t.start();开启了t线程,t线程就直接获取到了执行权。

    就调用自己的run方法。

    指定到sleep(3000).t线程冻结3秒,这是t线程就是释放了执行权。

    那么主线程开始执行打印了Thread Test,主线程结束。

    等到3秒后,t线程打印MyThread running ,然后t线程结束。

程序结束。

线程间通讯:

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

wait:

notify();

notifyAll();

都使用在同步中,因为要对持有监视器(锁)的线程操作。

所以要使用在同步中,因为只有同步才具有锁。

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

     因为这些方法在操作同步线程时,都必须要标识它们所操作线程持有的锁,

只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒。

不可以对不同锁中的线程进行唤醒。

    也就是说,等待和唤醒必须是同一个锁。

而锁可以是任意对象,所以可以被任意对象调用的方法定义Object类中。

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

    将同步Synchronized替换成显示Lock操作。

    将Object中的wait,notify notifyAll,替换了Condition对象。

该对象可以Lock锁 进行获取。

Lock:替代了Synchronized

lock 

unlock

newCondition()

Condition:替代了Object wait notify notifyAll

await();

signal();

signalAll();

stop方法已经过时.

如何停止线程?

    只有一种,run方法结束。

    开启多线程运行,运行代码通常是循环结构。

    只要控制住循环,就可以让run方法结束,也就是线程结束。

特殊情况:

    当线程处于了冻结状态。

    就不会读取到标记。那么线程就不会结束。

    当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。

强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。

Thread类提供该方法 interrupt()中断线程;

    将处于冻结状态的线程中断清除,重新回到运行状态

    setDaemon守护线程,让处于等待中的线程结束。

join:

    当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。

join可以用来临时加入线程执行。

    setPriority() 设置优先级

    Thread.yield();  停止正在执行的线程,运行其他线程。







  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于C++&OPENCV 的全景图像拼接 C++是一种广泛使用的编程语言,它是由Bjarne Stroustrup于1979年在新泽西州美利山贝尔实验室开始设计开发的。C++是C语言的扩展,旨在提供更强大的编程能力,包括面向对象编程和泛型编程的支持。C++支持数据封装、继承和多态等面向对象编程的特性和泛型编程的模板,以及丰富的标准库,提供了大量的数据结构和算法,极大地提高了开发效率。12 C++是一种静态类型的、编译式的、通用的、大小写敏感的编程语言,它综合了高级语言和低级语言的特点。C++的语法与C语言非常相似,但增加了许多面向对象编程的特性,如类、对象、封装、继承和多态等。这使得C++既保持了C语言的低级特性,如直接访问硬件的能力,又提供了高级语言的特性,如数据封装和代码重用。13 C++的应用领域非常广泛,包括但不限于教育、系统开发、游戏开发、嵌入式系统、工业和商业应用、科研和高性能计算等领域。在教育领域,C++因其结构化和面向对象的特性,常被选为计算机科学和工程专业的入门编程语言。在系统开发领域,C++因其高效性和灵活性,经常被作为开发语言。游戏开发领域中,C++由于其高效性和广泛应用,在开发高性能游戏和游戏引擎中扮演着重要角色。在嵌入式系统领域,C++的高效和灵活性使其成为理想选择。此外,C++还广泛应用于桌面应用、Web浏览器、操作系统、编译器、媒体应用程序、数据库引擎、医疗工程和机器人等领域。16 学习C++的关键是理解其核心概念和编程风格,而不是过于深入技术细节。C++支持多种编程风格,每种风格都能有效地保证运行时间效率和空间效率。因此,无论是初学者还是经验丰富的程序员,都可以通过C++来设计和实现新系统或维护旧系统。3

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值