您的位置:首页 > 编程语言 > Java开发

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

2015-08-29 17:47 471 查看
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类建立线程对象
       
aad4
        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>
问题解决!!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息