Java学习笔记之
多线程(一)——线程的创建
线程的概念:线程是程序中一个单一的顺序控制流程,是进程内一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指运行中的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
线程在控制着进程的执行,一个进程中至少有一个线程,一个线程必须有一个父进程。
为了正确有效地使用线程,必须理解线程的各个方面并了解Java 实时系统。必须知道如何提供线程体、线程的生命周期、实时系统如 何调度线程、线程组、什么是幽灵线程(Demo nThread)。
Java VM启动时会有一个进程java.exe,该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中,该线程称之为主线程。
JVM启动不止一个线程,还有负责垃圾回收机制的线程。
守护线程
守护线程是特殊的线程,一般用于在后台为其他线程提供服务.
Java中,isDaemon():判断一个线程是否为守护线程.
Java中,setDaemon():设置一个线程为守护线程.
Thread
类。该子类应重写Thread
类的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>
运行结果:
Demo d = new Demo();//创建一个线程
//d.start();
d.run();
线程的状态:
<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>
<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>
问题解决!!