------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! -------
多线程
在同一刻运行多个任务的能力称为多任务(multitasking),每一个任务称为一个线程(thread),可以同时运行一个以上线程的程序称为多线程程序(multithreaded).线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行.一个进程中至少有一个线程.
进程:是一个正在执行中的程序.每一个进程都有一个执行顺序.该顺序是一个执行路径,或者叫一个控制单元.
JavaVM启动的时候有一个进程java.exe,该进程中至少有一个负责java程序的执行,而且这个线程运行的代码存在于main()方法中,该线程称之为主线程.
创建新执行线程有两种方法.一种方法是将类声明为Thread的子类.该子类应重写thread类的run方法.接下来可以以分配并启动该子类的实例.另一种方法是声明实现Runnable接口的类.该类然后实现run方法.然后可以分配该类的实例,在创建Thread时作为一个参数来传递并启动.
为什么要覆盖run方法?
Thread类用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码该存储功能就是run方法,
创建线程
1.实现代码:
public class CreateThreadDemo
{
public static void main(String[] args)
{
//创建一个线程
NewThread nt = new NewThread();
nt.start();//开启线程,并调用run()方法
for (int i = 0; i < 60; i++)
{
System.out.println("------world" + i);
}
}
}
class NewThreadextends Thread
{
public void run()
{
for (int i = 0; i < 60; i++)
{
System.out.println("hello" + i);
}
}
}
从上面运行的结果可以看出多线程的一个特性:随机性.并且多线程都在获取CPU的使用权,CPU执行到谁,谁就运行.CPU在做着快速的切换,以达到看上去同时运行的效果.但某一时刻只能有一个程序在运行(多核除外)
2.实现代码:
步骤:
Ø 定义类实现Runnable接口
Ø 覆盖Runnable接口中的run方法
Ø 通过Thread类建立线程对象
Ø 将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
Ø 调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
public class CreateThreadByInterface
{
public static void main(String[] args)
{
// 实例化对象
SaleTicket st = new SaleTicket();
// 创建线程
Thread thread1 = new Thread(st);
Thread thread2 = new Thread(st);
Thread thread3 = new Thread(st);
// 启动线程并运行st.run方法
thread1.start();
thread2.start();
thread3.start();
}
}
class SaleTicketimplements Runnable
{
int ticket = 100;
public void run()
{
while (ticket > 0)
{
/*
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}*/
System.out.println(Thread.currentThread().getName()+"---"
+ (ticket--));
}
}
}
实现方式和继承方式有什么区别?
继承Thread:线程代码存放在Thread子类run方法中.
实现Runnable:线程代码存放在接口的子类run方法中.
实现方式好处:避免了单继承的局限性,在定义线程时,建议使用实现方式.从上面的代码可以看出实现方式把ticket资源独立出来,多个线程操作同一个资源.
线程安全
以上图形描述了线程的四种状态:当调用sleep()和wait()方法时线程被冻结,当调用wait()冻结线程时要唤醒线程则必须使用notity()方法.当调用stop()方法时和线程运行结束时,该线程消亡.几个线程同时运时而某一刻CPU中只能有一个在运行,那行其它的线程将处于阻塞状态或者叫临时状态.当几个线程共享数据时,当线程正处理共享数据还没有执行完,而另一个参与进来执行,那么就将可能出现线程安全问题,导致共享数据错误.
如把上面代码: /*
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}*/
注释取消那么再运行程序,就很有可能会出现票已经卖出完毕却还在卖票的情况.打印结果很可能出现ticket等于0或0以下.这就出现了安全问题.
解决办法:对多条操作共享数据的语句,只能让一个线程都执行完.在执行过程中,其它的线程不可以参与执行.java对于多线程的安全问题提供了专业的解决方式”同步代码块”
synchronized(Object obj)
{
//需要被同步的代码……
}
对象如同锁,持有锁的线程可以在同步中执行,没有持有锁的线程即便获取CPU的执行权.也进不去因为没有获取锁.
同步的前提:
1. 必须要有两个或以上的线程;
2. 必须是多个线程使用同一个锁.
必须保证同步中只能有一个线程在运行.同步虽然解决了安全问题,但是也降低了效率.因为每次都得去判断锁.较为消耗资源.就好比门上了锁.开锁需要时间一样.同步有两种表现形式:同步代码块,同步函数
同步(锁)演示
实现代码:
/**
* 需求:有两个储户分别存300元,每次存100元,存3次.
* 目的:练习线程安全
* 如何分析线程安全:
* 1.明确哪些代码是多线程代码
* 2.明确共享数据
* 3.明确多线程运行代码中哪些语句是操作共享数据的
*/
public class SynchronizedDemo
{
public static void main(String[] args)
{
Depositor dep = new Depositor();//实例化对象
Thread th1 = new Thread(dep);//创建线程
Thread th2 = new Thread(dep);//创建线程
th1.start();// 开启线程并运行run方法
th2.start();
}
}
// 银行类
class Bank
{
int sum;
public synchronized void add(int n)//同步函数
{
//synchronized (this)同步代码块
//{
sum += n;
try
{
Thread.sleep(10);//停10毫秒
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println("sum:" +sum);
//}
}
}
// 储户类
class Depositorimplements Runnable
{
// 用户到银行去存钱,实例化对象
Bank bank = new Bank();
public void run()
{
for (int i = 0; i < 3; i++)
{
// 调用银行的存款功能 .每次存100,存3次
bank.add(100);
}
}
}
函数需要被对象调用,那么函数都有一个所属对象引用(this),因此同步函数所使用的锁是this.如果同步函数被静态修饰后使用的锁不再是this.因为静态方法中不可以定以this.静态进内存中,没有本类对象,但一定有该类对应的字节码文件对象.因而使用用的锁是该静态所对应的字节码文件对象.那么该静态所使用的锁就是(类名.class),类型是Class
静态修饰的函数的使用的锁代码演示:
public class SynchronizedDemo3
{
public static void main(String[] args)throws InterruptedException
{
// 实例化对象
SaleTicket2 st = new SaleTicket2();
// 创建线程
Thread thread1 = new Thread(st);
Thread thread2 = new Thread(st);
// 启动线程并运行st.run方法
thread1.start();
//因为有可能主线程执行完毕的时候,第一个线程并未执行,因此先冻结
Thread.sleep(10);
st.flag = false;
thread2.start();
}
}
class SaleTicket2 implements Runnable
{
static int ticket = 100;
boolean flag = true; //定义一个标记,让线程在不同的地方同步运行
public void run()
{
if (flag)
{
while (true)
{
//synchronized使用的是字节码文件对象(SaleTicket2.class)
synchronized (SaleTicket2.class)
{
if (ticket > 0)
{
try
{
Thread.sleep(4);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "this&&" + (ticket--));
}
}
}
}
else
{
show();
}
}
//静态修饰的同步函数,他所使用的锁是SaleTicket2.class
public static synchronized void show()
{
while (ticket > 0)
{
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"show**"
+ (ticket--));
}
}
}
线程停止的方法只有一种run运行结束(stop()已过时)!