线程是程序执行的最小单元。
区别: 进程是操作系统进行资源处理和分配的最小单位,而一个进程可以包含多个线程,并共享进程的资源。
介绍之前,我们需要理解并行和并发的定义:
并行:同一个时刻有多个线程进行。
并发:同一个时间段内有多个线程进行。
多线程指的是一个进程可以包含多个并发的线程(同一个时刻只有一个线程运行)。例如酷狗,我们可以一边听歌一边搜索自己喜欢的歌曲。多线程的存在能够让进程及时处理我们多项的请求,提高应用程序的利用率。
多线程编程需要了解到多线程运行面临的问题。
Java实现多线程有两个方式。
线程类MyThread:
public class MyThread extends Thread{ @Override public void run() { for(int x=0;x<200;x++){ System.out.println(x); } } }
主类Demo:
public class Demo { public static void main(String[] args) { MyThread mt = new MyThread();//新建线程类对象 MyThread mt1 = new MyThread(); mt.start();//调用start()方法 mt1.start(); } }
ps,为什么不直接调用线程类的run()方法,而调用start()方法?
run()方法只是封装了多线程执行的操作,只是一个普通方法。
start()方法是启动线程执行的方法,由JVM自动调用run()方法。
步骤:1,编写实现Runnable 接口的类,重写run()方法
public class ThreadRunnable implements Runnable { public void run(){}}
2,在主类中新建线程类对象,obj
ThreadRunnable tr = new ThreadRunnable();
3,新建Thread类t,将obj作为t的构造参数
Thread t = new Thread(tr);
4,调用t的start()方法。
由于Java只允许单类继承,故多选用实现Runnable接口的方法创建多线程,事实上Thread类也是接口Runnable的实现类。
public class Thread extends Object implements Runnable
线程状态控制常用到的方法如下:
t.sleep(1000);让线程t睡眠1000毫秒,即1秒。
A.start();
A.join();//try catch
B.start();
C.start();
A执行完之后B和C才可以执行
A.yield();A暂停一下,时间不确定,让同等级的线程优先运行。
A.interrupt();把线程的状态中止,并抛出 InterruptedException 。跳出阻塞的部分可以继续执行接下来的代码。
interrupt()只是改变中断状态而已. interrupt()不会中断一个正在运行的线程。这一方法实际上完成的是,给受阻塞的线程抛出一个中断信号,这样受阻线程就得以退出阻塞的状态。更确切 的说,如果线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞, 它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。
如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到InterruptedException异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。
ps:对于一个正在运行的线程如果想要其结束运行,可以使用标志位,让线程跳出从而结束运行,示例如下:
public class ThreadFlag extends Thread { public volatile boolean exit = false; public void run() { while (!exit); } }
我们来看下面的一段代码:
public class ThreadRunnable implements Runnable { private static int D = 100;//D是静态变量由多个线程共享。public void run() { while(true){if(D>0){ try { Thread.currentThread().sleep(100); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"---"+(D--)); } } }//run }
由于D是静态变量,它由ThreadRunnable的所有对象访问,每个对象对它进行输出,并减1的操作,直到D为0(可将D假想成某直达列车的票,每 个ThreadRunnable对象是一个售票窗口)假设有三个窗口如下:
public class ThreadRunnableDemo { public static void main(String[] args) { ThreadRunnable tr = new ThreadRunnable();//创建接口对象 Thread t1 = new Thread(tr,"窗口1");//创建Thread类,将上述对象作为构造参数 Thread t2 = new Thread(tr,"窗口2");//创建Thread类,将上述对象作为构造参数 Thread t3 = new Thread(tr,"窗口3");//创建Thread类,将上述对象作为构造参数 t1.start();//启动start方法 t2.start(); t3.start(); } }
输出:
.... 窗口1---97 窗口2---97 .... .... 窗口3---2 窗口1---1 窗口2---0 窗口3----1
出同号票的原因分析:
出现0号和负号票的原因分析:
这就产生了线程不安全的问题,产生线程不安全的场景:
多个线程访问同一资源,并对资源进行多条语句操作就有可能引发线程不安全。
概括:多个线程;同一资源;不是原子操作
前两个条件我们无法改变,我们有的解决思路就是将线程对资源操作语句封装成原子操作(不会被打断)。将操作封装成原子操作。
Java使用synchronized关键字。
使用规范:
public void run() { while(true){ synchronized(new Object()){ if(D>0){ try { Thread.currentThread().sleep(100); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"---"+(D--)); } } } }//run()
下面考虑更复杂的情况------死锁
死锁顾名思义就是加的锁打不开,一般发生在当两个线程互相拿着对方的锁即锁嵌套,造成两个线程一直处于等待中。死锁示例:
经典的生产者和消费者问题
问题描述:生产者producer生产资源,消费者customer消耗资源呢,我们设计的程序最低保证消费者在消耗资源时必须保证有资源。
设计思想:消费者和生产者共用一个锁,消费者一直消费资源,直到剩余资源数小于规定(0或者业务目标),消费者线程进入等待,直到生产者生产资源后将自己唤醒。
实现:
Producer类
package producer; import java.util.ArrayList; public class Producer implements Runnable { private ArrayList<String> al; public Producer(ArrayList<String> al){ this.al = al; } @Override public void run() { while(true){ synchronized (al) { while(al.size()>0){ try { al.wait();//只要有资源,生产线程就wait() } catch (InterruptedException e) { e.printStackTrace(); } } for(int i=0;i<10;i++){ al.add(""+i); } al.notify();//生产完成,唤醒等待线程即消费者线程 } } } }
Customer类
package producer; import java.util.ArrayList; public class Customer implements Runnable { private ArrayList<String> al; public Customer(ArrayList<String> al){ this.al = al; } @Override public void run() { while(true){ synchronized (al) { while(al.size()<1){ try { al.wait();//如果没资源,消费者线程就wait() } catch (InterruptedException e) { e.printStackTrace(); } } while(al.size()>0){ System.out.println("消费者:"+al.size()); al.remove(0); } al.notify();//消费没了,唤醒等待线程即生产者线程 } } } }
调用类Demo
package producer; import java.util.ArrayList; public class Demo { private static ArrayList<String> al= new ArrayList<String>(); public static void main(String[] args) { Producer p = new Producer(al); Customer c = new Customer(al); Thread producer = new Thread(p); Thread customer = new Thread(c); producer.start(); customer.start(); } }
实际业务场景中线程的寿命都很短暂,例如对于网站访问,每个用户请求是一个线程,如果来一个用户,进行一套线程的创建、就绪等动作会严重影响
服务器的响应效率,鉴于此,Java中有了线程池的解决办法,它的思想是程序初始运行时在一个容器内新建固定数量的线程,当用到时从容器内取出一个线程,
线程执行完之后再放回到容器内,实质是以空间换时间,这个容器在Java中就被称为线程池。
Java实现线程池
Executors类工厂类
方法:
public static ExecutorService new FixedThreadPool(int nThreads);//该方法返回一个含有n个线程的线程池接口
线程池接口:ExecutorService
方法:
submit(Runnable task);//将一个线程类加入到线程池
结束:shutdown()
ps:福利:
原文:http://www.cnblogs.com/moye13/p/5693337.html