Java学习笔记之——多线程(一)

Java学习笔记之 

多线程(一)——线程的创建


       线程的概念:线程是程序中一个单一的顺序控制流程,是进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程

线程在控制着进程的执行,一个进程中至少有一个线程,一个线程必须有一个父进程。


        为了正确有效地使用线程,必须理解线程的各个方面并了解Java 实时系统。必须知道如何提供线程体、线程的生命周期、实时系统如 何调度线程、线程组、什么是幽灵线程(Demo nThread)。

        Java VM启动时会有一个进程java.exe,该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程称之为主线程。

        JVM启动不止一个线程,还有负责垃圾回收机制的线程。

守护线程
        守护线程是特殊的线程,一般用于在后台为其他线程提供服务.
        Java中,isDaemon():判断一个线程是否为守护线程.
        Java中,setDaemon():设置一个线程为守护线程.

幽灵线程
        任何一个Java线程都能成为幽灵线程。它是作为运行于同一个进程内的对象和线程的服务提供者。例如,HotJava浏览器有一个称为" 后台图片阅读器"的幽灵线程,它为需要图片的对象和线程从文件系统或网络读入图片。 幽灵线程是应用中典型的独立线程。它为同一应用中的其他对象和线程提供服务。幽灵线程的run()方法一般都是无限循环,等待服务请求。



创建新执行线程有两种方法:
方法一:继承Thread 类。该子类应重写Thread 类的run 方法
        步骤:
                1.定义类继承Thread
                2.复写Thread类中的run方法(Thread中的run方法用于存储线程要执行的代码。复写的目的:将自定义的代码存储于run方法,让线程运行)
                3.调用线程的strat方法,该方法用于启动线程,调用run方法

<span style="font-size:18px;">import java.lang.Thread;
class Demo extends Thread{
	public void run()
	{
		for(int x=0; x<40; x++)
			System.out.println("demo run--"+x);
	}
}

class TreadDemo{
	public static void main(String[] args)
	{
		Demo d = new Demo();//创建一个线程
		d.start();
		
		for(int x=0; x<40; x++)
			System.out.println("Hello World!--"+x);
	}
}</span>
运行结果:

                
                ....
每次运行结果不尽相同,因为多个线程都获取cpu的执行权,cup执行到谁,谁运行。某一时刻,只能一个程序运行(多核除外)。cpu在做着快速切换,已达到同时运行的效果。多线程的随机性,互相抢夺cpu的执行权。

start和run的区别:
        如果不调用start方法,而直接调用run方法(如下),则程序会顺序先执行完Demo类中的输出,在执行主函数中的输出,此时已经不是多线程。因为创建的线程没有启动。
		Demo d = new Demo();//创建一个线程
		//d.start();
		d.run();

线程的状态:
          

获取线程对象和名称:
        static Thread currentThread():获取当前线程。
        线程都有自己默认的名称。 Thread-编号,该编号从0开始。
        getName():获取线程名称。
        自定义线程名称:setName()或者Thread类提供的传递线程名称的构造函数Thread(String name)

<span style="font-size:18px;">import java.lang.Thread;
class Demo extends Thread{
	
	Demo(String name)
	{
		super(name);//调用父类
	}
	public void run()
	{
		for(int x=0; x<40; x++)
			System.out.println(this.getName()+" run--"+x);//或者Thread.currentThread().getName()
	}
}

class TreadDemo{
	public static void main(String[] args)
	{
		Demo d1 = new Demo("one");//创建一个线程
		Demo d2 = new Demo("two");
		d1.start();
		d2.start();
		for(int x=0; x<40; x++)
			System.out.println("Hello World!--"+x);
	}
}</span>

方法二:实现Runnable接口
        步骤:
                1.定义类实现Runnable接口
                2.覆盖Runnable接口中的run方法
                3.通过Thread类建立线程对象
                4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
                5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法

如下是一个多窗口售票程序:
<span style="font-size:18px;">import java.lang.Thread;
class Ticket implements Runnable
{
	private int tick = 100;//总票数
	public void run()
	{
		while(true)
		{
			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();
	}

}</span>
打印结果:
                 

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


       实现方式避免了单继承的局限性。如:student类继承了person类,这时如果student类需要实现多线程,则无法再继承Thread类。

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


存放代码位置不一样:

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

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


多线程的安全问题:

如果在上述代码中,执行if(tick>0)时,线程被挂起,转而执行其他线程,这时就可能出现错误。

稍微改动一下代码,利用Thread中的sleep()函数让线程执行到if语句时停一会:

			<span style="font-size:18px;">if(tick>0)
			{
				try{Thread.sleep(10);}catch(Exception e){}
				System.out.println(Thread.currentThread().getName()+" sale:"+tick--);
			}</span>
执行结果:
                 

打印出了0,-1,-2等错票,多线程运行出现安全问题。

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

一般情况下,在程序测试阶段,很难发现这类安全错误。


解决办法:

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


Java关于多线程安全问题解决方式:同步代码块

synchronized(对象)

           需要被同步的代码

对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即使获取cpu执行权也进不去,必须要设置锁。


同步的前提:

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

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


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

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

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


上述程序代码改动如下:

<span style="font-size:18px;">class Ticket implements Runnable
{
	private int tick = 100;//总票数
	Object obj = new Object();
	public void run()
	{
		while(true)
		{
			synchronized(obj)//取值为0或1,表示开关
			{
				if(tick>0)
				{
					try{Thread.sleep(10);}catch(Exception e){}
					System.out.println(Thread.currentThread().getName()+" sale:"+tick--);
				}
			}
			
		}
	}
}</span>
问题解决!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值