系统进行资源分配和调用的独立单位。每一个进程都有他自己的内存空间和系统资源。单进程的计算机只能做一件事情,而我们现在的计算机都可以做多件事情。举例:一边玩游戏(游戏进程),一边听音乐(音乐进程)。
也就是说现在的计算机都是支持多进程的,可以在一个时间段内执行多个任务。
线程是依赖于进程而存在。
(360是一个进程,同时运行360的不同功能是多线程);
一个进程中至少有一个线程。
(4)目的:开启多个线程是为了同时运行多部分代码。
为了提高应用程序的使用率。程序的执行其实都是在抢CPU的资源,CPU的执行权。
多个进程是在抢这个资源,而其中的某一个进程如果执行路径比较多(线程多),就会有更高的几率抢到CPU的执行权。
我们是不敢保证哪一个线程能够在哪个时刻抢到,所以线程的执行有随机性。
注意并行和并发: 垃圾回收线程(finalize())也要先启动,否则很容易会出现内存溢出。 如果多线程中的某一条线程发生错误,会显示异常,并停止这条线程,但其他线程不受影响.
C/C++去调用系统功能创建进程,然后由Java去调用进程实现多线程1.多线程优点:
2.多线程缺点:
3.多线程有几种实现方案,分别是哪几种? 
  两种。 
  1.继承Thread类 
  2.实现Runnable接口
扩展一种:实现Callable接口。这个得和线程池结合。(一般可以不答)
假如我们的计算机只有一个CPU,那么CPU 在某一个时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。
线程有两种调度模型:
Java使用的是抢占式调度模型。
| 方法 | 定义 | 
|---|---|
public final int getPriority() | 
  获取线程对象的优先级 | 
public final void setPriority(int newPriority) | 
  设置线程的优先级 | 
public class ThreadPriorityDemo {public static void main(String[] args) {ThreadPriority tp1 = new ThreadPriority();ThreadPriority tp2 = new ThreadPriority();//设置线程名tp1.setName("东方不败");tp2.setName("岳不群");// 获取默认优先级System.out.println(tp1.getPriority());//设置线程优先级tp1.setPriority(10);tp2.setPriority(1);System.out.println(tp1.getPriority());System.out.println(tp2.getPriority());tp1.start();tp2.start();}}///////////////5-----默认是510---设置后15东方不败:0东方不败:1
注意: 
 线程优先级高仅仅表示线程获取的 CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到比较好的效果。
sleep(long millis) | 
  线程休眠,单位是毫秒(ms)。(时间到了继续运行) | 
|---|---|
join() | 
  线程加入,只有这个线程完毕,其他线程才可以继续 | 
yield() | 
  线程礼让,暂停当前线程,执行其他线程 | 
setDaemon(boolean on) | 
  后台线程:将该线程标记为守护线程或用户线程必须在启动线程前调用(一旦只剩下守护线程,后台线程立即结束) | 
stop() | 
  中断线程 | 
interrupt() | 
  中断线程,把线程的状态终止,并抛出一个异常,不会影响后续代码运行 | 
toString() | 
  Thread.*currentThread*().toString();Thread[Thread-0,5,main] | 
public class SleepThread extends Thread{public void run(){for(int i = 0 ; i < 5 ;i++){try{Thread.sleep(2000);//此线程sleep时,会运行其他线程//只能用try catch,不能throws,因为父类没抛这个异常,子类也不能抛//Thread.sleep(1000);//可以直接写sleep(1000);而省略Thread}catch(Exception ex){}System.out.println(getName()+" "+i);}}}SleepThread s1 = new SleepThread();SleepThread s2 = new SleepThread();s1.setName("意");s2.setName("而");s1.start();s2.start();

等待该线程终止,只有这个线程完毕,其他线程才可以继续
public class SleepThread extends Thread{public void run(){for(int i = 0 ; i < 50 ;i++){System.out.println(getName()+" "+i);}}}//__________________SleepThread s1 = new SleepThread();SleepThread s2 = new SleepThread();SleepThread s3 = new SleepThread();s1.setName("111");s2.setName("222");s3.setName("333");s1.start();s1.join();//将1线程加入s2.start();s3.start();

注意: 
理论上是一个线程执行一次后,等待下一个线程执行,然后第一个线程再执行第二次,……实际上可能有"误差"
public void run() {for (int x = 0; x < 100; x++) {System.out.println(getName() + ":" + x);Thread.yield();}}//____________________________________________________ThreadYield ty1 = new ThreadYield();ThreadYield ty2 = new ThreadYield();ty1.setName("林青霞");ty2.setName("刘意");ty1.start();ty2.start();

| 方法 | 定义 | 
|---|---|
setDaemon(boolean on) | 
  将该线程标记为守护线程或用户线程。   当正在运行的线程只剩下守护线程时,Java 虚拟机退出。 该方法必须在启动线程前调用。  | 
关羽张飞守护刘备,刘备完成走了,关羽张飞就也走了
ThreadDaemon td1 = new ThreadDaemon();ThreadDaemon td2 = new ThreadDaemon();td1.setName("关羽");td2.setName("张飞");// 设置守护线程(注意:必须在start()方法之前设置,否则会有异常!!)td1.setDaemon(true);td2.setDaemon(true);td1.start();td2.start();Thread.currentThread().setName("刘备");//改一改main线程的名字for (intx = 0; x < 5; x++) {System.out.println(Thread.currentThread().getName() + ":" + x);}但是不会立马结束,还会运行几次

| 方法 | 定义 | 
|---|---|
stop(): | 
  让线程停止,这一个线程停止 | 
interrupt() | 
  停止程序,运行catch部分代码 | 
public class ThreadStopDemo {public static void main(String[] args) {ThreadStop ts = new ThreadStop();ts.start();try {Thread.sleep(3000);ts.stop();//3s后会停止程序} catch (InterruptedException e) {e.printStackTrace();}}}
--3s后程序停止
public static void main(String[] args) {ThreadStop ts = new ThreadStop();ts.start();try {Thread.sleep(3000);ts.interrupt();//结束线程,运行catch内容} catch (InterruptedException e) {e.printStackTrace();}}
 
好处:不影响后续代码执行(继续运行catch之后的)

CPU同一时刻只能处理一个线程,多线程是多个线程轮流运行,允许运行但在等待的线程处于阻塞状态。
| 方法 | 定义 | 
|---|---|
| sleep | 需要指定睡眠时间,单位是毫秒(ms)。(时间到了继续运行); | 
| wait() | 等待,自己无法醒来,用notify(),可以唤醒; | 
1.继承Thread类2.重写run方法3.直接创建Thread的子类对象创建线程。4.调用start方法开启线程并调用线程的任务run方法执行。
不是类中的所有代码都需要被线程执行的,为了区分哪些代码能够被线程执行,java提供了Thread类中的
run()用来包含那些被线程执行的代码。(代码若想被多线程执行,必须写(封装)在run里面)一般来说,被线程执行的代码肯定是比较耗时的。
| 方法 | 定义 | 
|---|---|
run() | 
  定义类时需要覆写的方法,定义的是线程中要运行的代码块 | 
start() | 
  开启线程,调用run方法 | 
getName() | 
  获取线程的名称(Thread-编号(从0开始))run和start都是Thread-0 | 
super(name) | 
  带参构造时,设置线程名称 | 
Thread.currentThread() | 
  返回正在运行的线程对象;Thread thread = Thread.currentThread(); | 
run():仅仅是封装被线程执行的代码,直接调用是普通方法,是按顺序运行 start():首先启动了线程,然后再由jvm去调用该线程的run()方法,是随机运行注意:
同一个线程只能调用一次!!(如my.start();只能用一次,第二次会显示异常IllegalThreadStateException)
//创建一个类,继承Thread,实现run方法public class MyThread extends Thread {public void run() {for (int x = 0; x < 10000; x++) {System.out.println(x);}}}//测试,调用线程直接new一个,start即可public class MyThreadDemo {public static void main(String[] args) {// 创建两个线程对象MyThread my1 = new MyThread();//开启一个新的线程MyThread my2 = new MyThread();my1.start();//执行线程my2.start();}}//-----这是三个线程,my1,my2,main

| 名字 | 方法 | 获取的线程 | 
|---|---|---|
| 获取main线程名称 获取当前线程名称  | 
  Thread.currentThread().getName() | main | 
| 当前的线程(main无法使用) | 方法1:Thread.currentThread().getName()  方法2:getName()  | 
  Thread-0 | 
public class MyThread extends Thread {public void run() {for (int x = 0; x < 100; x++) {System.out.println(getName() + ":" + x);}}}
public class MyThread extends Thread {public MyThread() {}////在这里可以设置线程名比如super("林");public MyThread(String name){super(name);}@Overridepublic void run() {for (int x = 0; x < 100; x++) {System.out.println(getName() + ":" + x);}}}
//在main方法中调用MyThread my1 = new MyThread();MyThread my2 = new MyThread();System.out.println("main线程:"+Thread.currentThread().getName());//获取主线程名称my1.start();my2.start();//*************************************************************************main线程:mainThread-1:0Thread-1:1
| 方法 | 定义 | 
|---|---|
| setName(String name) | 设置线程的名称(main线程无法改名) | 
无参构造设置线程名
MyThread my1 = new MyThread();MyThread my2 = new MyThread();my1.setName("林青霞");my2.setName("刘意");my1.start();my2.start();////////林青霞:0刘意:0...
带参构造
MyThread my1 = new MyThread("林青霞");MyThread my2 = new MyThread("刘意");my1.start();my2.start();//****************林青霞:13刘意:0
1.为什么要给Thread类的构造函数传递Runnable的子类对象?
因为线程的任务都封装在Runnable接口子类对象的run方法中。所以要在线程对象创建时就必须明确要运行的任务。
2.为什么可以避免由于Java单继承带来的局限性
比如说,某个类已经有父类了,而这个类想实现多线程,但是这个时候它已经不能直接继承Thread类了(接口可以多实现implements,但是继承extends只能单继承),它的父类也不想继承Thread因为不需要实现多线程。
3.实现Runnable接口的好处:
1,实现Runnable接口。2,重写run方法,将线程的任务代码封装到run方法中。3,创建本类的对象4,创建Thread类的对象,并把3步骤的对象作为构造参数传递。
class Demo implements Runnable{ //通过接口的形式完成。public void run(){ //覆盖接口中的run方法for(int x=0; x<100; x++)System.out.println(Thread.currentThread().getName()+"....."+x);//获取名称,只能间接用}}
class ThreadDemo{public static void main(String[] args) {Demo d = new Demo();//注意:MyRunnable对象只需要创建一个即可,多个Thread对象可以接收同一个MyRunnable对象Thread t1 = new Thread(d);//通过Thread类创建线程对象,并传递Runnable。Thread t2 = new Thread(d);t1.start();t2.start();}}//-----结果---------Thread-0...0Thread-0...1
Demo my = new Demo();Thread t1 = new Thread(my, "林青霞"); //创建,初始化线程名Thread t2 = new Thread(my, "刘意");t1.start();t2.start();//****************************林青霞...84刘意...64
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。就会导致线程安全问题的产生。(例如,卖票,多线程,可能把数据卖到-1,因为各线程互相独立)
注:线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程时不可以参与运算的。必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。(同一时间,只能有一个线程参与运算---同步机制)
StringBuffer sb = new StringBuffer();Vector<String> v = new Vector<String>();Hashtable<String, String> h = new Hashtable<String, String>();(在定义时就锁了,因此效率低)
注意:Vector是线程安全的时候才去考虑使用的,但是即使要安全,也不用
以前的:List
list1 = new ArrayList ();// 线程不安全 
public static <T> List<T> synchronizedList(List<T> list)List<String> list2 = Collections.synchronizedList(new ArrayList<String>()); // 线程安全
某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
public class SellTicket extends Thread {// 定义100张票// 为了让多个线程对象共享这100张票,我们其实应该用静态修饰private static int tickets = 100;public void run() {// 定义100张票// 每个线程进来都会走这里,这样的话,每个线程对象相当于买的是自己的那100张票,这不合理,所以应该定义到外面// 是为了模拟一直有票while (true) {if (tickets > 0) {System.out.println(getName() + "正在出售第" + (tickets--) + "张票");}}}}
调用
public class SellTicketDemo {public static void main(String[] args) {// 创建三个线程对象SellTicket st1 = new SellTicket();SellTicket st2 = new SellTicket();SellTicket st3 = new SellTicket();// 给线程对象起名字st1.setName("窗口1"); st2.setName("窗口2"); st3.setName("窗口3");// 启动线程st1.start(); st2.start(); st3.start();}}
----
说明的是,通过继承Thread类来实现题中的需求并不是很好(tickets要用static修饰,并不太好),其实用Runnable接口更好地进行数据分离
public class SellTicket implements Runnable {// 定义100张票private int tickets = 100;public void run() {while (true) {if (tickets > 0) {System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");}}}}
调用
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();}}

这种代码会有安全隐患,比如一个窗口获取票后,过来一段时间才订,在这段时间其他窗口就运行了,此处以sleep模拟,让每个线程休息一段时间
if (tickets > 0) {// 为了模拟更真实的场景,我们稍作休息try {Thread.sleep(100); // t1就稍作休息,t2就稍作休息} catch (InterruptedException e) {e.printStackTrace();}System.out.println(......);}}}}
但是出问题了!!!
CPU的一次操作必须是原子性的(在读取tickets--的原来的数值和减1之后的中间挤进了两个线程而出现重复)
随机性和延迟导致的(三个线程同时挤进一个循环里,tickets--的减法操作有可能在同一个循环中被执行了多次而出现越界的情况,比如说tickets要大于0却越界到了-1)
也就是说,线程1执行的同时线程2也可能在执行,而不是线程1执行的时候线程2不能执行。
这就是线程的安全问题
同一时间,只能有一个线程访问该代码;
同步可以解决安全问题的根本原因就在那个对象synchronized上。该对象如同锁的功能。(对象锁,同步锁)
格式:
synchronized(对象){需要被同步的代码 ;}
Object obj = new Object();//同步,必须作为成员变量, 不能放在run内,public void run(){synchronized(obj){…}}
public void run(){synchronized(this或Ticket.class){…}}
public synchronized void add(int num){...}
任意对象票是按顺序减少的且没有了同票和负票
//卖票,共有100张票,多个线程同时卖票,一张票只能卖一次public class SellTicket implements Runnable {// 定义100张票private int tickets = 100;//创建锁对象private Object obj = new Object();public void run() {while (true) {synchronized (obj) {//必须用在run外面定义的obj,而不能用new Object,那样不是同一把锁,还会出错if (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+ "正在出售第" + (tickets--) + "张票");}}}}}
调用
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();}}
 
        票是按顺序减少的且没有了同票和负票 
public synchronized void add(int num){ … }
同步方法用的锁:this静态方法的锁对象是:当前类名.class,可以用对象.getClass()方法获取也可以用 类名.class表示;建议使用同步代码块。
public static synchronized void show(){}(其中num要该为static)
//定义出售的票源public class Tickets implements Runnable{private int ticket = 100;public void run(){while(true){payTicket();}}public synchronized void payTicket(){if( ticket > 0){try{Thread.sleep(10);}catch(Exception ex){}System.out.println(Thread.currentThread().getName()+" 出售第 "+ticket--);}}}
会出错,因为第一个synchronized的锁是d,而第二个同步方法的锁是this,会出错
- 方法:
 
- 方法1.把第一个的锁换成:
 this- 方法2.在第二个方法锁的第一行加上同样的锁
 synchronized(d)
public class Tickets implements Runnable{private int ticket = 100;private int x = 0;//run方法定义public void run() {while (true) {if(x%2==0){synchronized (d) {//thisif (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票 ");}}}else {sellTicket();}x++;}}//-------------测试-----------private static synchronized void sellTicket() {if (tickets > 0) {try {// Thread.sleep(100);//延时} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票 ");}}}
在总票数和同步方法上添加
static出错,静态方法锁不能用this,需要用的是当前类的class对象:SellTicket.class
public class Tickets implements Runnable{private static int tickets = 100;// 用静态方法锁,ticket必须加静态private int x = 0;public void run() {while (true) {if (x % 2 == 0) {synchronized (this) {// SellTicket.classif (tickets > 0) {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票 ");}}} else {sellTicket();}x++;}}private static synchronized void sellTicket() {if (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票 ");}}}
两个人到银行存钱,每人存3次,每次100‘
class Bank {// 建立银行类private int sum;// 金库public synchronized void add(int num) {// 同步函数(第二种写法),存钱sum = sum + num;try {Thread.sleep(10);} catch (InterruptedException e) {} // 延时等待,使顺序System.out.println("sum=" + sum);// 显示金库余额}}class Cus implements Runnable {// 定义线程类private Bank b = new Bank();public void run() {// 覆盖run方法for (int x = 0; x < 3; x++) {// 存钱,存三次,每次100b.add(100);}}}// 循环有次数限制,不会出现死循环class BankDemo {public static void main(String[] args) {Cus c = new Cus();// 实例化线程类Thread t1 = new Thread(c);// ThreadThread t2 = new Thread(c);t1.start();// 开启线程t2.start();}}
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
| 方法 | 定义 | 
|---|---|
| void lock() | 获取锁 | 
| void unlock() | 释放锁 | 
ReentrantLock是Lock的实现类(序列化类)
public class SellTicket implements Runnable {//实现接口private int tickets = 100;// 定义锁对象private Lock lock = new ReentrantLock();public void run() {while (true) {try {// 加锁lock.lock();if (tickets > 0) {try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");}} finally {// 释放锁lock.unlock();// 放在finally中,使释放锁必须进行}}}}


1.定义锁对象类,两个锁A和B
public class MyLock {public static final Object lockA=new Object();public static final Object lockB=new Object();}
2.线程任务类
public class ThreadTask implements Runnable {int x = new Random().nextInt(2);// 0,1 获取[0,2)之间随机值// 指定线程要执行的任务代码public void run() {while (true) {if (x % 2 == 0) {// 情况一synchronized (MyLock.lockA) {System.out.println("if-LockA");synchronized (MyLock.lockB) {System.out.println("if-LockB");System.out.println("if大口吃肉");} }} else {// 情况二synchronized (MyLock.lockB) {System.out.println("else-LockB");synchronized (MyLock.lockA) {System.out.println("else-LockA");System.out.println("else大口吃肉");} } }x++;} }}
3.测试类
public class ThreadDemo {public staticvoid main(String[] args) {// 创建线程任务类对象ThreadTask task = new ThreadTask();// 创建两个线程Thread t1 = new Thread(task);Thread t2 = new Thread(task);// 启动线程t1.start();t2.start();}}
定义两个锁:
public class LockA {// 做一个私有构造器,保证锁是私有的private LockA() {}// 为了让其他类可以调用,这样调用,用final使其他类不能修改// 只能通过类名调用静态成员获得public static final LockA locka = new LockA();}///////////////////public class LockB {private LockB() {}public static final LockB lockb = new LockB();}
线程任务类
public class DeadLock implements Runnable {// 写死循环private int i = 0;public void run() {while (true) {if (i % 2 == 0) {// 偶数// 先进入A同步,再进入B同步synchronized (LockA.locka) {System.out.println("if...locka");// 先进入A同步synchronized (LockB.lockb) {System.out.println("if...lockb");}}} else {// 先进入B同步,再进入A同步synchronized (LockB.lockb) {System.out.println("else...lockb");// 先进入B同步synchronized (LockA.locka) {System.out.println("else...locka");}}}i++;// 一次奇数一次偶数}}}
测试类
public class DeadLockDemo {public static void main(String[] args) {DeadLock dead = new DeadLock();Thread t0 = new Thread(dead);Thread t1 = new Thread(dead);t0.start();t1.start();}}//------结果---if...lockaif...lockbelse...lockbif...locka 形成死锁
线程间通讯:多个线程在处理同一资源,但是任务却不同。(不同线程处理同一个资源) 
例程: 
 
//资源,一对一,输入一个,输出一个class Resource {String name;String sex;}
生产者
// 输入,生产者(输入),对资源对象Resource中成员变量赋值class Input implements Runnable {//实现接口Resource r;// 反参数传递,因为:输入输出是同一个资源,不能用new了,可以加privateInput(Resource r) { this.r = r; }// 反参数传递,构造函数,可以加publicpublic void run() {int x = 0;while (true) {synchronized (r) {// 解决线程问题,与输出锁相同, 同步必须在while里面if (x == 0) {// x两次切换赋值,使得好像输入很多r.name = "mike";r.sex = "nan";} else {r.name = "丽丽";r.sex = "女女";} }x = (x + 1) % 2;// x的0、1变化}}}
消费者
//输出,消费者(输出)class Output implements Runnable {Resource r;Output(Resource r) { this.r = r;}public void run() {while (true) {synchronized (r) {// 和输入要用相同的锁,同步必须在while里面System.out.println(r.name + "....." + r.sex);}}}}
测试
class aaa {public static void main(String[] args) {Resource r = new Resource();// 创建资源。Input in = new Input(r);// 创建任务Output out = new Output(r);Thread t1 = new Thread(in);// 创建线程,执行路径。Thread t2 = new Thread(out);t1.start();// 开启线程t2.start();}}

虽然数据安全了,但是呢,一次一大片不好看(获取同一个数据一次而输出多次,也就是set一次却get了多次),我就想依次的一次一个输出(也就是set一次get一次)。
如何实现呢?
通过Java提供的等待唤醒机制解决。
在Object类中提供了方法:
| 方法 | 定义 | 
|---|---|
wait() | 
  让线程处于冻结状态,被wait的线程会被存储到线程池中。(释放CPU执行资格和执行权)this.wait(); | 
notify() | 
  唤醒线程池中一个线程(任意)。notify(); | 
notifyAll() | 
  唤醒线程池中的所有线程。notifyAll(); | 
这些方法都必须定义在同步中。因为这些方法是用于操作线程状态的方法。必须要明确到底操作的是哪个锁上的线程。
因为这些方法的调用是依赖于锁对象的,而同步代码块的锁对象是任意锁。任意的对象调用的方式一定定义在Object类中。
| wait | sleep | 
|---|---|
| 指定时间 | 都可以 | 
| 暂停时 | 释放执行权,释放锁。(别的线程可以进去) | 
方法
| 方法 | 定义 | 
|---|---|
if | 
  if(flag),只有一次,会导致不该运行的线程运行了。出现了数据错误的情况。(等待后,获得执行权不会再次进行判断flag)(一对一使用) | 
while | 
  while(flag),解决了线程获取执行权后,是否要运行!(等待后,获得执行权可以再次进行判断flag)(多对多使用) | 
notify | 
  只能唤醒一个线程,如果本方唤醒了本方法,没有意义。而且while判断标记+notify会导致死锁。(一对一使用) | 
notifyAll | 
  解决了本方线程一定会唤醒对方线程的问题。(多对多使用) | 
资源类
class Resource {private String name;// 安全优化,私有化private String sex;// 安全优化,私有化private boolean flag = false;// 一对一输入输出public synchronized void set(String name, String sex) {if (flag) // 若资源有内容,暂时不输入,等输出后再赋值try { this.wait();} catch (InterruptedException e) {}// wait需要异常处理this.name = name;this.sex = sex;flag = true;this.notify();// 赋值完成,唤醒输出}public synchronized void out() {if (!flag)// 若资源无内容,暂时不输出try { this.wait();} catch (InterruptedException e) {}System.out.println(name + "..." + sex);flag = false;// 输出完成,定义flag无内容this.notify();// 唤醒输入}}
输入
class Input implements Runnable {Resource r;// 反参数传递,因为:输入输出是同一个资源,不能用new了Input(Resource r) {this.r = r;}// 反参数传递,构造函数public void run() {int x = 0;while (true) {if (x == 0)// x两次切换赋值,使得好像输入很多r.set("mike", "nan");elser.set("丽丽", "女女女女女女");x = (x + 1) % 2;// x的0、1变化} }}
输出
class Output implements Runnable {Resource r;Output(Resource r) {this.r = r;}public void run() {while (true) { r.out();} }}
资源
public class Student {String name;int age;boolean flag; // 判断默认情况是没有数据,如果是true,说明有数据}
生产者
public class SetThread implements Runnable {// 生产者private Student s;private int x = 0;public SetThread(Student s) {this.s = s;}public void run() {while (true) {synchronized (s) {if (s.flag) { // 判断有没有try {// 若有s.wait(); // 等待(必须是锁的wait),并释放锁,释放CPU执行权;} catch (InterruptedException e) {e.printStackTrace();}}if (x % 2 == 0) {// 若没有,或没有了之后释放锁s.name = "林青霞";s.age = 27;} else {s.name = "刘意";s.age = 30;}x++; // x=1s.flag = true;// 生产完,修改标记,使进入消费s.notify(); // 唤醒t2,抢CPU的执行权。} // t1有,或者t2有}}}
消费
public class GetThread implements Runnable {// 消费private Student s;public GetThread(Student s) {this.s = s;}public void run() {while (true) {synchronized (s) {if (!s.flag) {// 若此时没有try {s.wait(); // 等待。并立即释放锁。将来醒过来的时候,是从这里醒过来} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(s.name + "---" + s.age);// 当有内容的时候,// 林青霞---27// 刘意---30s.flag = false; // 修改标记防止再次进入消费s.notify();// 唤醒线程唤醒t1}}}}
测试
public class StudentDemo {public static void main(String[] args) {// 创建资源Student s = new Student();// 设置和获取的类SetThread st = new SetThread(s);GetThread gt = new GetThread(s);// 线程类Thread t1 = new Thread(st);Thread t2 = new Thread(gt);// 启动线程t1.start();t2.start();}}//-----结果----林青霞---27刘意---30林青霞---27刘意---30林青霞---27
资源
class Resource {private String name;// 商品名称private int count = 1;// 烤鸭编号private boolean flag = false;// 标记public synchronized void set(String name) {// 生产设置烤鸭名字while (flag)// while进行判断,如果正在烤try {this.wait();}catch (InterruptedException e) {} // 等待t1 t0this.name = name + count;// 烤鸭+编号----若已经消费了count++;// 下一个编号2 3 4System.out.println(Thread.currentThread().getName() + "..生产者.." + this.name);// 生产烤鸭1flag = true;notifyAll();// 多线程,只唤醒一个可能出现死锁,需要唤醒所有线程}public synchronized void out() {// 消费t3while (!flag)// 如果正在卖try {this.wait();}catch (InterruptedException e) {} // 等待t2 t3System.out.println(Thread.currentThread().getName() + "...消费者........" + this.name);// 消费烤鸭1flag = false;notifyAll();}}
生产者
class Producer implements Runnable {// 生产者private Resource r;Producer(Resource r) {this.r = r;}// 构造public void run() {while (true) {r.set("烤鸭");} // 生产烤鸭}}
消费者
class Consumer implements Runnable {// 消费者private Resource r;Consumer(Resource r) {this.r = r;}public void run() {while (true) { r.out();} // 消费烤鸭}}
测试
class ProducerCustomerDemo {public static void main(String[] args) {Resource r = new Resource();// 实例化资源Producer pro = new Producer(r);// 生产Consumer con = new Consumer(r);// 消费Thread t0 = new Thread(pro);// 线程t0负责生产Thread t1 = new Thread(pro);Thread t2 = new Thread(con);Thread t3 = new Thread(con);t0.start();t1.start();t2.start();t3.start();}}//----结果----Thread-1..生产者..烤鸭136416Thread-2...消费者........烤鸭136416Thread-0..生产者..烤鸭136417Thread-3...消费者........烤鸭136417Thread-1..生产者..烤鸭136418Thread-2...消费者........烤鸭136418Thread-0..生产者..烤鸭136419Thread-3...消费者........烤鸭136419Thread-1..生产者..烤鸭136420Thread-2...消费者........烤鸭136420Thread-0..生产者..烤鸭136421
jdk1.5以后将同步和锁封装成了对象。并将操作锁的隐式方式定义到了该对象中,将隐式动作变成了显示动作。
Lock lock = new ReentrantLock();
Condition接口:出现替代了Object中的wait、notify、notifyAll方法。将这些监视器方法单独进行了封装,变成Condition监视器对象。可以任意锁进行组合。可以有多个监视器
| 接口 | 方法 | 定义 | 
|---|---|---|
| Lock接口 | Lock lock = new ReentrantLock(); | |
| lock() | 获取锁。lock.lock(); | |
| unlock() | 释放锁,通常需要定义finally代码块中。lock.unlock(); | |
| Condition接口 | Condition con = lock.newCondition(); | |
| await() | 代替wait()功能,con.await(); | |
| signal() | 代替notify()功能,con.signal(); | |
| signalAll() | 代替notifyAll()功能,con.signalAll(); | 
资源
//jdk1.5以后将同步和锁封装成了对象。import java.util.concurrent.locks.*;class Resource {private String name;private int count = 1;private boolean flag = false;Lock lock = new ReentrantLock();// 创建一个锁对象。// 通过已有的锁获取两组监视器,一组监视生产者,一组监视消费者。Condition producer_con = lock.newCondition();// 监视生产锁Condition customer_con = lock.newCondition();// 监视消费锁public void set(String name) {// 去掉synchronized同步lock.lock();// 获取锁---同步try {// 异常处理while (flag)// 用while,循环检测try {producer_con.await();} catch (InterruptedException e) {} // 生产等待this.name = name + count;count++;System.out.println(Thread.currentThread().getName() + "...生产者5.0..." + this.name);// 生产烤鸭flag = true;customer_con.signal();// 生产完,释放另一组锁的一个,运行消费} finally {lock.unlock();} // 释放锁}public void out() {lock.lock();// 获取锁---同步try {while (!flag)// 用while,循环检测try {customer_con.await();} catch (InterruptedException e) {} // 消费等待System.out.println(Thread.currentThread().getName() + "...消费者.5.0......." + this.name);// 消费烤鸭flag = false;producer_con.signal();// 消费后,释放另一组锁的一个,运行生产} finally {lock.unlock();} // 释放锁}}
它可以对一批线程进行分类管理,Java允许程序直接对线程组进行控制。
我们也可以给线程设置分组
| 方法 | 定义 | 
|---|---|
| public final ThreadGroup getThreadGroup() | 获取所属的线程组 | 
| public final String getName() | 获取名字 | 
| Thread(线程组名, 类名, 自定义线程) | 创建线程组 | 
| Void destroy() | 销毁此线程组及其所有子组 | 
| Void interrupt() | 中断此线程组中的所有线程 | 
| Void setDaemon() | 变为后台线程:守护线程:只有守护线程会结束程序 | 
| Void setMaxPriority() | 设置最高优先级 | 
public class MyRunnable implements Runnable {public void run() {for (int x = 0; x < 100; x++) {System.out.println(Thread.currentThread().getName() + ":" + x);}}}
测试
MyRunnable my = new MyRunnable();Thread t1 = new Thread(my, "林青霞");Thread t2 = new Thread(my, "刘意");// 获取所在线程组的名字// 线程类里面的方法:public final ThreadGroup getThreadGroup()ThreadGroup tg1 = t1.getThreadGroup();//获取线程组ThreadGroup tg2 = t2.getThreadGroup();// 线程组名的方法:public final String getName()String name1 = tg1.getName();//获取线程组名字String name2 = tg2.getName();System.out.println(name1);System.out.println(name2);//获取主线程线程组名字System.out.println(Thread.currentThread().getThreadGroup().getName());

默认情况下,所有的线程都属于主线程组main
获取线程组名字,自己建立的线程属于main线程组
创建一个线程组
创建其他线程的时候,把其他线程的组指定为我们自己新建线程组
//创建一个新的线程组ThreadGroup tg = new ThreadGroup("这是一个新的组");MyRunnable my = new MyRunnable();//Thread(ThreadGroup group, Runnable target, String name)Thread t1 = new Thread(tg, my, "林青霞");Thread t2 = new Thread(tg, my, "刘意");System.out.println(t1.getThreadGroup().getName());System.out.println(t2.getThreadGroup().getName());//通过组名称设置后台线程,表示该组的线程都是后台线程---守护线程:若只剩下守护线程,程序结束//设置守护线程tg.setDaemon(true);

----创建成功
(1****)原因:创建和销毁线程成本是比较高的,而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
(2)线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,从JDK5开始,Java内置支持线程池
    线程池主要用来**解决线程生命周期开销问题和资源不足问题**。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使**应用程序响应更快**。另外,通过适当的调整线程中的线程数目可以**防止****出现资源不足**的情况。
(3)JDK5新增了一个Executors工厂类来产生线程池,有如下几个方法
| 方法 | 定义 | 
|---|---|
| public static ExecutorService newCachedThreadPool() | 创建一个具有缓存功能的线程池 | 
| public static ExecutorService newFixedThreadPool(int nThreads) | 创建一个可重用的,具有固定线程数nThreads的线程池;  ExecutorService pool = Executors.newFixedThreadPool(2);  | 
| public static ExecutorService newSingleThreadExecutor() | 创建一个只有单线程的线程池,相当于上个方法的参数是1 | 
| Future submit(Runnable task) | pool.submit(new MyRunnable()); | 
| Future submit(Callable task) | 调用 | 
| Void shutdown() | 结束线程池 | 
这些方法的返回值是ExecutorService对象,该对象表示一个线程池,可以执行Runnable对象或者Callable对象代表的线程。
它提供了如下方法
import java.util.concurrent.ExecutorService;public class MyRunnable implements Runnable {public void run() {for (int x = 0; x < 100; x++) {System.out.println(Thread.currentThread().getName() + ":" + x);}}}//_____________________________________________________________________public class ExecutorsDemo {public static void main(String[] args) {// 创建一个线程池对象,控制要创建几个线程对象。ExecutorService pool = Executors.newFixedThreadPool(2);// 可以执行Runnable对象或者Callable对象代表的线程pool.submit(new MyRunnable());//调用线程池中线程,提交一次调用一个线程,用完返回pool.submit(new MyRunnable());pool.shutdown();//结束线程池,没有他,程序一直不关闭}}

run方法没有返回值,不能抛出异常,而这个接口的方法call()可以。
这里指定的泛型其实是call()方法的返回值类型。
Future<String> f = es.submit(new ThreadPoolCallable());String s = f.get();//get是泛型
案例
public class MyCallable implements Callable {//可以直接设置call的类型 Callable<String>public String call() throws Exception {for (int x = 0; x < 100; x++) {System.out.println(Thread.currentThread().getName() + ":" + x);}return null;}}
测试
public class CallableDemo {public static void main(String[] args) {//创建线程池对象ExecutorService pool = Executors.newFixedThreadPool(2);//可以执行Runnable对象或者Callable对象代表的线程pool.submit(new MyCallable());pool.submit(new MyCallable());//结束线程池pool.shutdown();}}

使用多线程技术,求和 , 两个线程,1个线程计算1+100,另一个线程计算1+200的和
public class MyCallable implements Callable<Integer> {private int number;public MyCallable(int number) {this.number = number;}public Integer call() throws Exception {//求1-number的和int sum = 0;for (int x = 1; x <= number; x++) {sum += x;}return sum;}}/////////////////////////public class CallableDemo {public static void main(String[] args) throws InterruptedException, ExecutionException {// 创建线程池对象ExecutorService pool = Executors.newFixedThreadPool(2);// 可以执行Runnable对象或者Callable对象代表的线程Future<Integer> f1 = pool.submit(new MyCallable(100));Future<Integer> f2 = pool.submit(new MyCallable(200));// V get()Integer i1 = f1.get();//获得结果---需要抛出异常Integer i2 = f2.get();System.out.println(i1);//显示结果System.out.println(i2);// 结束pool.shutdown();}}

(1)匿名内部类的格式:
new 类名或者接口名() {重写方法;};
(2)本质:是该类或者接口的子类对象。
public class ThreadDemo {public static void main(String[] args) {// 1.继承Thread类来实现多线程-----只用一次new Thread() {public void run() {for (int x = 0; x < 100; x++) {System.out.println(Thread.currentThread().getName() + ":"+ x);}}.start();}}}

// 2.实现Runnable接口来实现多线程new Thread(new Runnable() {public void run() {for (int x = 0; x < 100; x++) {System.out.println(Thread.currentThread().getName() + ":" + x);}}}) {}.start();}
加上这个,就是两个线程互相抢了 

// 更有难度的new Thread(new Runnable() {public void run() {for (int x = 0; x < 100; x++) {System.out.println("hello" + ":" + x);}}}) {}.start();
hello也加入了抢,共三个 

new Thread(new Runnable() {public void run() {for (int x = 0; x < 100; x++) {System.out.println("world" + ":" + x);}}}.start();
全部运行的话,不会运行hello,只会运行world,还是3个 
 
方法3:
Runnable r = new Runnable(){public void run(){System.out.println("###");}};new Thread(r).start();
定时器是一个应用十分广泛的线程工具,可用于调度多个定时任务以后台线程的方式执行。在Java中,可以通过Timer和TimerTask类来实现定义调度的功能
在开发中一般不会用Timer,因为太弱,开发中一般用框架:
Quartz是一个完全由java编写的开源调度框架。
Timer---在util包中---定时器类
| 方法 | 定义 | 
|---|---|
| public Timer() | 创建一个新的计时器 | 
| public void schedule(TimerTask(任务),Date (时间)) | 安排在指定的时间执行的任务(1次) | 
| public void schedule(TimerTask(任务),long(延迟)) | 安排在指定延迟后执行的任务(1次) | 
| public void schedule(TimerTask (任务),long (时间),long period) | 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行 | 
| public void schedule(TimerTask (任务),Date (时间),long (延迟)) | 安排指定的任务在指定的时间开始进行重复的固定延迟执行 | 
TimerTask--在Object包中:由Timer安排为一次执行或重复执行的任务
| 方法 | 定义 | 
|---|---|
| public abstract void run() | 此计时器要执行的操作 | 
| public boolean cancel() | 终止此计时器 | 
| Int purge() | 从此计时器的任务队列中移除所有已取消的任务 | 
| Long sheduleExecutionTime() | 返回此任务最近实际执行是已安排执行时间 | 
// 做一个任务class MyTask extends TimerTask {private Timer t;//创建一个新的计时器public MyTask(){}//构造public MyTask(Timer t){ this.t = t;}//构造方法public void run() {System.out.println("beng,爆炸了");t.cancel();//终止要在任务执行完结束}}public class TimerDemo {public static void main(String[] args) {// 创建定时器对象Timer t = new Timer();//3s后运行,并结束任务t.schedule(new MyTask(t), 3000);}}
循环爆炸:3秒后执行爆炸第一次,每隔2秒再继续炸---不用加结束了,不然会只运行一次
// 做一个任务class MyTask2 extends TimerTask {public void run() {System.out.println("beng,爆炸了");}}public class TimerDemo2 {public static void main(String[] args) {// 创建定时器对象Timer t = new Timer();// 3秒后执行爆炸任务第一次,每隔2秒再继续炸t.schedule(new MyTask2(), 3000, 2000);}}
//需求:在指定的时间删除我们的指定目录(我使用项目路径下的demo)(只删除1次)class DeleteFolder extends TimerTask {// 定义删除类public void run() {File srcFolder = new File("demo");deleteFolder(srcFolder);// 调用删除方法t.cancel();// 终止要在任务执行完结束}// 递归删除目录public void deleteFolder(File srcFolder) {File[] fileArray = srcFolder.listFiles();// 文件数组if (fileArray != null) {// 若有内容for (File file : fileArray) {// 遍历if (file.isDirectory()) {// 若是文件夹deleteFolder(file);// 遍历} else {// 若是文件—删除,并输出名字System.out.println(file.getName() + ":" + file.delete());}} // 若没有内容,删除文件夹System.out.println(srcFolder.getName() + ":" + srcFolder.delete());}}}public class TimerTest {public static void main(String[] args) throws ParseException {Timer t = new Timer();String s = "2014-11-27 15:45:00";SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");Date d = sdf.parse(s);// 定义删除时间t.schedule(new DeleteFolder(), d);// 在指定时间删除指定内容}}
//多线程下的单例//饿汉式--单例模式,简单,使用class Single {// 公式private static final Single s = new Single();private Single() {}public static Single getInstance() { return s;}}// 懒汉式--延迟加载单例模式,麻烦,但是考试多(技术含量高)// 加入同步为了解决多线程安全问题。// 加入双重判断是为了解决效率问题。class Single {private static Single s = null;private Single() {}public static Single getInstance() {// 同步使同一时间只进入一个线程if (s == null) {// 加入双重判断是为了解决效率问题。synchronized (Single.class) {// 加入同步为了解决多线程安全问题。// 不可以用this.getClass(),因为是非静态的if (s == null) s = new Single();}}return s;}}class SingleDemo {public static void main(String[] args) {Single s1 = Single.getInstance();// 单例调用Single s2 = Single.getInstance();// 用s1,s2调用单例其他方法}}
任务中都会有循环结构,只要控制住循环就可以结束任务。控制循环通常就用定义标记(条件:while之类)来完成。(若有wait(处于冻结状态),sleep(时间有时很长)无法结束)
interrupt(中断)方法。可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。 但是强制动作会发生了InterruptedException,记得要处理。
class StopThread implements Runnable {private boolean flag = true;public synchronized void run() {while (flag) {try {System.out.println("线程");wait();} // 有wait,处于冻结状态,无法正常结束catch (InterruptedException e) {// 中断异常处理System.out.println(Thread.currentThread().getName() + ".." + e);flag = false;// 中断后,结束}System.out.println(Thread.currentThread().getName() + "......++++");}}public void setFlag() {flag = false;}}class StopThreadDemo {public static void main(String[] args) {StopThread st = new StopThread();Thread t1 = new Thread(st);Thread t2 = new Thread(st);t1.start();// run中,直接进入wait状态,t2.setDaemon(true); // t2设置为守护线程(后台线程)在启动线程前调用t2.start();int num = 1;for (;;)// 无限循环{if (++num == 5) {// 若num==20,线程结束System.out.println("interrupt前");t1.interrupt();// t1中断,运行run中的中断异常处理System.out.println("interrupt后");break;}System.out.println("main...." + num);}System.out.println("over");// main结束}}//-----结果---线程//线程1输出,之后1进入waitmain....2//main输出main....3main....4interrupt前//主函数,顺序interrupt后//主函数,顺序(interrupt在线程中,并行)over//主函数线程//线程2输出,之后2进入waitThread-1.....java.lang.InterruptedException//interrupt1,运行run的catchThread-1......++++//线程1,顺序输出
1.如果错误 错误发生在哪一行?
class Test implements Runnable{public void run(Thread t){}}----------------------------------------------------错误在第一行,应该被abstract修饰:class abstract Test implements Runnable{原因:这个类实现了接口,但是抽象方法run没有进行覆盖,那么类要定义为抽象类
2.输出的是什么?
class ThreadTest {public static void main(String[] args) {new Thread(new Runnable() {public void run() {System.out.println("runnable run");}}) {public void run() {System.out.println("subThread run");}// run进行了覆写}.start();}}-------------------------------------------------输出:subThread run若是后面{}里没有内容,输出:runnable run正常写法:new Thread(){public void run(){System.out.println("run覆写");}}.start();
























原文:https://www.cnblogs.com/ziyue7575/p/12193933.html