-----------android培训、java培训、java学习型技术博客、期待与您交流!------------
多线程的使用需知道这四个概念,
进程:进程是系统系统进行资源分配和调用的独立单位,每一个进程都有它自己的内存空间和系统资源,其实就是正在运行的程序。多进程可以同时执行多个任务,提高CPU的使用率。
线程:线程是程序的执行单元,也叫程序的执行路径,是程序使用CPU的最基本单位。多线程是程序有多条执行路径,能提高程序的使用率。
并行:指逻辑上的同时发生,在某个时间段内运行多个程序。
并发:物理上的同时发生,指在某个时间点上运行多个程序。
程序实现多线程有两种方式:继承Thread类和实现Runnable接口。
方式1:继承Thread类
a:将类声明为Thread的子类;
b:该子类重写Thread类的run()方法;(run()方法用来包含哪些被线程执行的代码)
c:创建该子类的对象;
d:启动线程,即使用该子类对象调用start()方法。
Thread类(java.lang包)
表示程序中的执行线程。
构造方法:
public Thread();
分配新的线程对象。
public Thread(String name);
创建一个新的线程对象,并指定名称。
public Thread(Runnable target);
根据一个Runnable接口的实现类对象创建一个新的线程对象。
public Thread(Runnable target,String name);
创建线程对象并指定名称。
成员方法:
public final String getName();
获取线程名称。
public final void setName(String name);
设置线程名称。
public static Thread currentThread();
返回当前正在执行的线程对象。
线程的优先级
Java使用的是抢占是调度模型,优先级高的线程获取的CPU时间片相对多一些。
public final int getPriority();
返回该线程对象的优先级。
public final void setPriority(int newPriority);
设置该线程对象的优先级。
线程的优先级范围是1~10,默认是5,当对线程设置了超范围的int值时,会出现异常IllegalArgumentException,原因是向方法传递了一个不正确的参数。
控制线程的方法:
public static void sleep(long millis) throws InterruptedException;
暂时让当前线程休眠,参数为休眠的毫秒值。
public final void join() throws InterruptedException;
加入线程,在程序运行中,一个线程对象调用此方法,则该线程执行完成后,其它线程才能执行。该方法在启动线程后调用有效果。
public static void yield();
礼让线程,暂停当前正在执行的线程对象,并执行其他线程。让多个线程的执行更和谐,但是不能靠它保证一人一次。
public final void setDaemon(boolean on);
守护线程,当某个线程被标记为守护线程,则当其他线程运行结束后,此线程也随即结束,如果所剩线程全部都是守护线程,则所有守护线程都结束运行。(该方法必须在启动线程前调用)
public final void stop();
让线程停止(已过时)
public void interrupt();
中断线程,把线程的状态终止,并抛出一个异常InterruptedException。
方式1举例
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">//自定义一个类继承Thread类 public class MyThread extends Thread { //将需要多线程运行的代码写入run()方法中。 public void run() { for (int x = 0; x < 100; x++) { System.out.println(getName() + " : " + x); //设置线程休眠0.02秒 try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } } } }</span></span></span>
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">//自定义类继承Thread类实现多线程 public class ThreadDemo { public static void main(String[] args) { MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); MyThread mt3 = new MyThread(); mt1.setName("NO1"); mt2.setName("NO2"); mt3.setName("NO3"); //调用start方法启动线程 mt1.start(); //设置加入线程mt1 try { mt1.join(); } catch (InterruptedException e) { e.printStackTrace(); } mt2.start(); mt3.start(); //输出主线程名称 System.out.println(Thread.currentThread().getName()+" : 主线程"); } }</span></span></span>
方式2:实现Runnable接口
a:自定义一个类,将其实现Runnable接口;
b:重写run()方法;
c:创建自定义类的对象;
d:创建Thread类的对象,并把c步骤的对象作为构造参数传递。
方式2举例
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">//自定义类实现Runnable接口 public class MyRunnable implements Runnable { //重写run()方法 public void run() { for(int x = 0;x<100;x++) { System.out.println(Thread.currentThread().getName()+" : " + x); } } }</span></span></span>
<span style="font-size:14px;"><span style="font-size:14px;"><span style="font-size:14px;">//自定义类实现Runnable接口,实现多线程 public class MyRunnableDemo { public static void main(String[] args) { //创建自定义类的对象 MyRunnable mr = new MyRunnable(); //创建Thread类对象,并指定线程名称 Thread t1 = new Thread(mr,"NO1"); Thread t2 = new Thread(mr,"NO2"); t1.start(); t2.start(); } }</span></span></span>
方式2与方式1的不同:
A:避免了有Java单继承带来的局限性;
B:适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码、数据有效分离,较好的体现了面相对象的设计思想。
线程安全问题的原因:
A:是否是多线程环境;
B:是否有共享数据;
C:是否有多条语句操作共享数据;
针对多条语句操作共享数据出现的线程安全问题,Java提供了同步代码块来解决:
synchronized(对象) {需要同步的代码}
同步代码块结局安全问题的根本原因在于那个锁对象,所以多个线程必须使用同一个锁对象。
同步的弊端:耗费资源,降低程序的运行效率。
当一个方法内的所有代码都需要同步时,可将该方法定义为同步方法,把synchronized关键字加在方法声明上即可。
同步代码块的锁对象:任意对象。
同步方法的锁对象:this
当同步方法是静态时的锁对象:当前类名.class(当前类的字节码文件对象)
模拟三个窗口同时售票,使用同步代码块解决线程安全问题,
<span style="font-size:14px;"><span style="font-size:14px;">//模拟售票,使用同步代码块解决安全问题 public class SellTicket implements Runnable { private int number = 100; private Object obj = new Object(); public void run() { synchronized (obj) { while (number > 0) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在出售第" + number-- + "张票"); } } } }</span></span>
<span style="font-size:14px;"><span style="font-size:14px;">//模拟三个窗口同时售票 public class SellTicketDemo { public static void main(String[] args) { SellTicket st = new SellTicket(); Thread t1 = new Thread(st,"窗口1"); Thread t2 = new Thread(st,"窗口2"); Thread t3 = new Thread(st,"窗口3"); t1.start(); t2.start(); t3.start(); } }</span></span>
加锁时的注意事项:
A:不同种类的线程都要加锁;
B:不同种类的线程加的锁必须是同一把。
Lock接口
概述:Lock接口提供了比synchronized更多功能的锁定操作
void lock();
获取锁。
void unlock();
释放锁。
等待唤醒机制:
在多个线程使用同一个资源时,它们的工作一般不同,Java提供了一种机制能使各个线程相互有效的利用资源,即等待唤醒机制。
该机制使用到的方法在Object类下:
public final void wait() throws InterruptedException;
使线程等待。(在等待中同时释放锁)
public final void notify();
唤醒单个线程。
public final void notifyAll();
唤醒全部线程。
使用等待唤醒机制控制两线程交替运行举例
<span style="font-size:14px;"><span style="font-size:14px;">//创建手机类 public class Phone { private String name; private String heart; private boolean flag; //创建set方法,让外界可通过此方法对手机属性赋值 public synchronized void set(String name, String heart) { if (this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.name = name; this.heart = heart; this.flag = true; this.notify(); } //创建get方法,让外界调用此方法输出手机属性 public synchronized void get() { if (!this.flag) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(name + "---" + heart); this.flag = false; this.notify(); } }</span></span>
<span style="font-size:14px;"><span style="font-size:14px;">//创建设置手机属性类 public class setThread implements Runnable { private int x = 0; private Phone p; public setThread(Phone p) { this.p = p; } public void run() { while (true) { if (x % 2 == 0) { p.set("华为", "Android"); } else { p.set("苹果", "ios"); } x++; } } }</span></span>
<span style="font-size:14px;"><span style="font-size:14px;">//创建获取类 public class getThread implements Runnable { private Phone p; public getThread(Phone p) { this.p = p; } public void run() { while (true) { p.get(); } } }</span></span>
<span style="font-size:14px;"><span style="font-size:14px;">//设置2个线程,一个对Phone赋值,一个输出值,使用等待唤醒机制控制两线程交替运行 public class PhoneDemo { public static void main(String[] args) { Phone p = new Phone(); setThread st = new setThread(p); getThread gt = new getThread(p); Thread t1 = new Thread(st); Thread t2 = new Thread(gt); t1.start(); t2.start(); } }</span></span>
线程组概述
Java中使用ThreadGroup类来表示线程组,它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。在默认情况下,所有线程都属于主线程组。
ThreadGroup类
public ThreadGroup(Stirng name);
使用构造方法创建一个新的线程组。
public final String getName();
返回此线程组的名称。
在Thread类中,有如下关于线程组的方法:
public Thread(ThreadGroup group,Runnable target,String name);
使用构造方法为指定线程分组。
public final ThreadGroup getThreadGroup();
获取当前线程组所在对象。
线程池
程序启动一个新线程成本是比较高的,而使用线程池可以很好的提高性能,线程池中每一个线程的代码结束后,线程会再次被回收到线程池中称为空闲状态,等待下一个对象来使用。
线程池由Executors工厂类中的方法产生,(JDK1.5)
public static ExecutorService newCachedThreadPool();
开启一个具有缓冲功能的线程池。
public static ExecutorService newFixedThreadPool(int nThread);
开区一个指定线程数的线程池。
public static ExecutorService newSingelThreadExecutor();
开启一个单线程的线程池。
这些方法可以创建一个线程池对象,使用此对象调用ExecutorService接口中的方法来创建多线程,
Future<?> submit(Runnable task);
使用Runnable接口的实现类对象创建线程。
<T> Future<T> submit(Callable<T> task);
使用Callable接口的实现类对象创建线程。
void shutdown();
关闭线程池。
在实际使用中,为了临时使用线程,往往使用匿名内部类方式创建线程,
<span style="font-size:14px;">//匿名内部类方式创建多线程 public class ThreadDemo { public static void main(String[] args) { //匿名内部类继承Thread new Thread() { public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + " : " + x); } } }.start(); //匿名内部类实现Runnable接口 new Thread(new Runnable() { public void run() { for (int x = 0; x < 100; x++) { System.out.println(Thread.currentThread().getName() + " : " + x); } } }).start(); } }</span>
定时器
可以安排任务在指定时间执行一次或多次,它依赖Timer类和TimerTask接口。
Timer类
public Timer();
创建一个计时器对象。
public void schedule(TimerTask,long delay);
在延迟指定时间后执行指定任务。
public void schedule(TimerTask, long delay, long period);
安排任务在指定时间以指定间隔重复执行。
public void cancel();
结束任务。
TimerTask抽象类
public abstract void run();
此计时器要执行的任务。