线程
来源:互联网 发布:php技术文档怎么写 编辑:程序博客网 时间:2024/05/16 20:28
一:线程和进程
线程是进程中的内容,每一个应用程序里面至少都有一个线程,每个线程都是一个执行路径.
线程存在的意义:可以使多部分代码同时进行
进程:是一个正在执行的程序,每个进程都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元
线程:就是进程中的一个独立的控制单元,线程在控制着进程的执行,一个进程中至少有一个线程
主线程:Java VM 启动的时候会有一个进程java.exe.该进程中至少一个线程负责java程序的执行。
而且这个线程运行的代码存在于main方法中。该线程称之为主线程。
扩展:其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回收机制的线程。
多线程的五个状态图:
二 线程的随机性:
发现运行结果每一次都不同。因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)cpu在做着快速的切换,以达到看上去是同时运行的效果。我们可以形象把多线程的运行行为在互相抢夺cpu的执行权。这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长,cpu说的算。
三 创建线程的两种方式:
实现Runnable接口和继承Thread类
实现方式和继承方式有什么区别呢?
实现方式和继承方式有什么区别呢?
实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
两种方式区别:
继承Thread:线程代码存放Thread子类run方法中。实现Runnable,线程代码存在接口的子类的run方法。
1.继承Thread类:
步骤:
(1),定义类继承Thread。
(2),复写Thread类中的run方法。目的:将自定义代码存储在run方法。让线程运行。
(3),调用线程的start方法,该方法两个作用:启动线程,调用run方法。
代码:
public class ThreadCreateMethod {public static void main(String[] args) {thread1 t1 = new thread1();//创建一个线程//t1.run();//仅仅是对象调用方法。而线程创建了,并没有运行。(属于单线程程序,只有start才能开启线程)t1.start();//开启线程并执行该线程的run方法。for(int x=0; x<60; x++)System.out.println("Hello World!--"+x);}}class thread1 extends Thread{public void run(){for(int x = 0;x<60;x++){System.out.println("thread1...is run"+x);}}}
1.1例子:创建两个线程,和主线程交替进行
static Thread currentThread():获取当前线程对象
getName():获取线程的名称
设置线程的名称:setName或者构造函数
代码:
public class ThreadGetName {public static void main(String[] args) {thread2 t1 = new thread2("t1");thread2 t2 = new thread2("t2");t1.start();t2.start();for(int x=0;x<60;x++)System.out.println("main----------"+x);}}class thread2 extends Thread{thread2(String name){super(name);}public void run(){for(int x = 0;x<60;x++){System.out.println((Thread.currentThread()==this)+"..."+this.getName()+"thread2 is run"+x);}}}
1.2 覆盖run方法的原因:
Thread类用于描述线程。该类就定义了一个功能,用于存储线程要运行的代码。该存储功能就是run方法。也就是说Thread类中的run方法,用于存储线程要运行的代码。
2.创建线程的第二种方式:实现Runable接口
步骤:
(1),定义类实现Runnable接口
(2),覆盖Runnable接口中的run方法。将线程要运行的代码存放在该run方法中。
(3),通过Thread类建立线程对象。
(4),将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数。
为什么要将Runnable接口的子类对象传递给Thread的构造函数?
因为,自定义的run方法所属的对象是Runnable接口的子类对象。
所以要让线程去指定指定对象的run方法。就必须明确该run方法所属对象。
(5),调用Thread类的start方法开启线程并调用Runnable接口子类的run方法。
示例:比如多个窗口同时卖票的例子:
public class TicketImplements {public static void main(String[] args) {//相当于4个窗口同时卖票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();}}class Ticket implements Runnable{//实现runnable接口private int ticket = 100;public void run() {while(true){if(ticket>0){System.out.println(Thread.currentThread().getName()+"...sale"+ticket--);}}}}2.1但是通过上面的代码发现,打印出0,-1,-2等错票多线程的运行出现了安全问题
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行java对于多线程的安全问题提供了专业的解决方式,就是同步代码块
四 线程同步
1.同步代码块格式
synchronized(对象)
{
需要被同步的代码
}
对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
同步的前提:
(1).必须要有两个或者两个以上的线程。
(2).必须是多个线程使用同一个锁。必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题
弊端:多个线程都需要判断锁,较为消耗资源。
2.卖票的示例同步后:public class TicketImplements {public static void main(String[] args) {//相当于4个窗口同时卖票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();}}class Ticket implements Runnable{//实现runnable接口private int ticket = 100;Object obj = new Object();public void run() {while(true){<span style="color:#ff0000;">synchronized(obj){if(ticket>0){System.out.println(Thread.currentThread().getName()+"...sale"+ticket--);}}</span>}}}3.多线程-同步函数
3.1 非静态的同步函数:
通过一个银行实例来认识同步代码块:
需求:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次。
如何找问题:
1,明确哪些代码是多线程运行代码。
2,明确共享数据。
3,明确多线程运行代码中哪些语句是操作共享数据的。
public class BankDemo {/** * @param args */public static void main(String[] args) {// TODO Auto-generated method stubCus c = new Cus();Thread t1 = new Thread(c);Thread t2 = new Thread(c);t1.start();t2.start();}}class Cus implements Runnable{Bank b = new Bank();public void run() {for(int x =0;x<3;x++){b.add(100);}}}class Bank{private int sum=0;<span style="color:#ff0000;">public synchronized void add(int n){//同步函数sum = sum+n;try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"sum"+sum);}</span>}通过上面的实例就会出现一个问题,同步函数用的是哪一个锁呢?
3.1.1 锁中的this
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。
用下面的例子来说明:(购票的例子)
public class ThisLockDemo {public static void main(String[] args) {Sale s = new Sale();Thread t1 = new Thread(s);Thread t2 = new Thread(s);t1.start();try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}s.flag = false;t2.start();}}class Sale implements Runnable{private int ticket = 100;boolean flag = true;public void run() {if(flag){while(true){<span style="color:#ff0000;">synchronized(</span><strong>this</strong><span style="color:#ff0000;">){if(ticket>0){try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"...code:"+ticket--);}}</span>}}else{while(true){show();}}}<span style="color:#ff0000;">public synchronized void show() {//</span><strong>this</strong><span style="color:#ff0000;">if(ticket >0){try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"...show:"+ticket--);}}</span>}
通过该程序进行验证。使用两个线程来买票。一个线程在同步代码块中。一个线程在同步函数中。都在执行买票动作。所以同步函数的锁就是this
3.2 静态的同步函数:
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不在是this。因为静态方法中也不可以定义this。
静态进内存是,内存中没有本类对象,但是一定有该类对应的字节码文件对象。类名.class 该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。 类名.class
public class StaticSaleDemo {/** * @param args */public static void main(String[] args) {Ticket1 t = new Ticket1();Thread t1 = new Thread(t);Thread t2 = new Thread(t);t1.start();try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}t.flag = false;t2.start();}}class Ticket1 implements Runnable{private static int ticket = 100;boolean flag = true;public void run(){if(flag){while(true){synchronized(<span style="color:#ff0000;">Ticket1.class</span>){if(ticket>0){try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"code"+ticket--);}}}}else{while(true)show();}}private static synchronized void show() {//<span style="color:#ff0000;">Ticket1.class</span>if(ticket>0){try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"code"+ticket--);}}}3.3.死锁:同步中嵌套着同步:
用同上的例子来说明:
public class DeadLock {/** * @param args */public static void main(String[] args) {Ticket2 t = new Ticket2();Thread t1 = new Thread(t);Thread t2 = new Thread(t);t1.start();try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}t.flag = false;t2.start();}}class Ticket2 implements Runnable{private static int ticket = 100;boolean flag = true;static Object o = new Object();public void run(){if(flag){while(true){synchronized(o){//同步代码块中嵌套同步函数(同步中嵌套同步)这里的对象是Object类型,锁定之后调用show方法show();//show方法的锁的对象是Ticket2.class等着这个解锁}}}else{while(true)show();}}private static synchronized void show() {//show方法的锁的对象是Ticket2.classsynchronized(o){//同步函数中嵌套同步代码块(同步中嵌套同步)这里的对象是Object类型,等着解锁,于是都等着解锁最终形成了死锁if(ticket>0){try {Thread.sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName()+"code"+ticket--);}}}}五.线程间的通信:
5.1其实就是多个线程操作同一个资源,但是操作的动作不同
举例:比如现在又一个人的对象(姓名和性别),一个class是存入人对象,一个class是输出人对象。线程间通信和安全的方法就是线程同步,并且引用同一个对象
public class Comunnication {/** * @param args */public static void main(String[] args) {per p1 = new per();input i = new input(p1);output o = new output(p1);Thread t1 = new Thread(i);Thread t2 = new Thread(o);t1.start();t2.start();}}class per{String name;String sex;}class input implements Runnable{private per r;input(per p){this.r = p;}public void run() {int x = 0;while(true){synchronized(r){//两个类操作同一个对象rif(x==0){r.name="lili";r.sex = "女";}else{r.name = "zhangsan";r.sex = "男";}}x= (x+1)%2;}}}class output implements Runnable{private per r;output(per r){this.r = r;}public void run() {while(true){synchronized(r){//两个类操作同一个对象System.out.println(r.name+"..."+r.sex);}}}}
5.2
问题一:wait(),notify(),notifyAll(),用来操作线程为什么定义在Object类中?
(1)这些方法存在同步中
(2)使用这些方法必须要标识所属的同步锁
(3)锁可以是任意对象,所以任意对象调用的方法一定定义在Object类中
问题二:
wait(),sleep()有什么区别
wait():释放资源,释放锁
sleep():释放资源,不释放锁
例一:
public class Comunnication {/** * @param args */public static void main(String[] args) {per p1 = new per();/*input i = new input(p1);output o = new output(p1);Thread t1 = new Thread(i);Thread t2 = new Thread(o);t1.start();t2.start();*///代码优化:new Thread(new input(p1)).start();new Thread(new output(p1)).start();}}class per{String name;String sex;boolean flag = false;}class input implements Runnable{private per r;input(per p){this.r = p;}public void run() {int x = 0;while(true){synchronized(r){//两个类操作同一个对象rif(r.flag==false)try {r.wait();//如果是假的话,就让当前线程等待,执行其他线程} catch (InterruptedException e) {e.printStackTrace();}if(x==0){r.name="lili";r.sex = "女";}else{r.name = "zhangsan";r.sex = "男";}x= (x+1)%2;r.flag = false;r.notify();//唤醒等待的第一个线程}}}}class output implements Runnable{private per r;output(per r){this.r = r;}public void run() {while(true){synchronized(r){//两个类操作同一个对象if(r.flag==true){try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(r.name+"..."+r.sex);r.flag = true;r.notify();}}}}例子二:多个生产者和消费者的问题
class CustomerAndProducer{public static void main(String[] args) {Resource r = new Resource();Producer pro = new Producer(r);Consumer con = new Consumer(r);Thread t1 = new Thread(pro);Thread t2 = new Thread(pro);Thread t3 = new Thread(con);Thread t4 = new Thread(con);t1.start();t2.start();t3.start();t4.start();}}class Resource{private String name;private int count = 1;private boolean flag = false;public synchronized void set(String name){while(flag)//用if可能导致生产两个才消费一个!(if只能用于生产者1个,消费者一个的情况)try{wait();}catch(Exception e){}this.name = name+"---"+count++;System.out.println(Thread.currentThread().getName()+"...生产者..........."+this.name);flag = true;this.notifyAll();}public synchronized void out(){while(!flag)try{wait();}catch(Exception e){}System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);flag = false;this.notifyAll();//用while时候再用notify可能会使全部等待,无法继续,所以用notifyAll会唤醒所有。就不会出现全部等待的问题}}class Producer implements Runnable{private Resource res;Producer(Resource res){this.res = res;}public void run(){while (true){res.set("+商品+");}}}class Consumer implements Runnable{private Resource res;Consumer(Resource res){this.res = res;}public void run(){while (true){res.out();}}}
(1)对于多个生产者和消费者,为什么要定义while判断标记
原因:让被唤醒的线程再一次判断标记
(2)为什么要定义notifyAll
因为需要唤醒对方的线程,因为只有用notify,容易出现唤醒本线程的情况。导致程序中的所有线程都等待。
5.3 jdk1.5唤起对方线程新特性:
之前用notifyAll唤醒的对方和我方的所有线程,但现在只想唤起对方的线程,所以JDK5.0升级版本(lock,unlock)
JDK1.5 中提供了多线程升级解决方案。
将同步Synchronized替换成现实Lock操作。
将Object中的wait,notify notifyAll,替换了Condition对象。
该对象可以Lock锁 进行获取。
该示例中,实现了本方只唤醒对方操作。
Lock:替代了Synchronized
lock
unlock
newCondition()
Condition:替代了Object wait notify notifyAll
await();
signal();
signalAll();
例子程序:实现了生产和消费交替进行
import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;import com.sun.org.apache.bcel.internal.generic.NEW;public class CustomerAndProducer2 {/** * @param args */public static void main(String[] args) {Res r = new Res();Pro p = new Pro(r);Cus1 c = new Cus1(r);Thread t1 = new Thread(p);Thread t2 = new Thread(c);t1.start();t2.start();}}class Res{private String name;private int count=1;private boolean flag = false;private Lock lock = new ReentrantLock();private Condition condition_pro = lock.newCondition();private Condition condition_con = lock.newCondition();public void set(String name){lock.lock();try {while(flag){try {condition_pro.await();} catch (InterruptedException e) {e.printStackTrace();}}this.name = name+"..."+count++;System.out.println(Thread.currentThread().getName()+"...customer"+this.name);flag = true;condition_con.signal();} finally{lock.unlock();}}public void out(){lock.lock();try {while(!flag){try {condition_con.await();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName()+"...producer"+this.name);flag = false;condition_pro.signal();} finally {lock.unlock();}}}class Cus1 implements Runnable{private Res r;Cus1(Res r){this.r = r;}public void run() {while(true){r.out();}}}class Pro implements Runnable{private Res r;Pro(Res r){this.r = r;}public void run() {while (true){r.set("+商品+");}}}
5.3停止线程:
(1)定义循环结束标记
因为线程运行的代码一般都是循环,只要控制了循环即可
(2)使用interrupt(中断)方法
该方法是结束线程的冻结状态,使线程回到运行状态中来
注意:stop方法已经过时不在使用
如何停止线程?
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法 interrupt();
只有一种,run方法结束。
开启多线程运行,运行代码通常是循环结构。只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于了冻结状态。就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态是,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。Thread类提供该方法 interrupt();
举一个例子来说明:
public class StopThread {public static void main(String[] args) {Stop s = new Stop();Thread t1 = new Thread(s);Thread t2 = new Thread(s);t1.start();t2.start();int num = 0;while(true){if(num++==5){//s.ChangeFlag();t1.interrupt();t2.interrupt();break;}System.out.println(Thread.currentThread().getName()+"..."+num);}System.out.println("over");}}class Stop implements Runnable{private boolean flag = true;public synchronized void run() {while(true){try{wait();}catch (InterruptedException e){System.out.println(Thread.currentThread().getName()+"...Exception");flag = false;}System.out.println(Thread.currentThread().getName()+"...run");}}public void ChangeFlag(){flag = false;}}
上面的结果:
main...1
main...2
main...3
main...4
main...5
over
Thread-0...Exception
Thread-0...run
Thread-1...Exception
Thread-1...run
5.4 线程守护main...2
main...3
main...4
main...5
over
Thread-0...Exception
Thread-0...run
Thread-1...Exception
Thread-1...run
setDaemon(true)
将线程标记为守护线程或用户线程。当正在运行的线程都是守护线程的时候,Java虚拟机退出,该方法必须在启动线程钱调用
比如上面的例子:
public class StopThread {/** * @param args */public static void main(String[] args) {Stop s = new Stop();Thread t1 = new Thread(s);Thread t2 = new Thread(s);t1.setDaemon(true);t2.setDaemon(true);t1.start();t2.start();int num = 0;while(true){if(num++==5){//s.ChangeFlag();//t1.interrupt();//t2.interrupt();break;}System.out.println(Thread.currentThread().getName()+"..."+num);}System.out.println("over");}}class Stop implements Runnable{private boolean flag = true;public synchronized void run() {while(true){try{wait();}catch (InterruptedException e){System.out.println(Thread.currentThread().getName()+"...Exception");flag = false;}System.out.println(Thread.currentThread().getName()+"...run");}}public void ChangeFlag(){flag = false;}}上面代码的结果
main...1
main...2
main...3
main...4
main...5
over
5.5 join,yield,setPriority
join:
当A线程执行到了B线程的.join()方法时,A就会等待。等B线程都执行完,A才会执行。
join可以用来临时加入线程执行。
join可以用来临时加入线程执行。
yield:暂停当前正在执行的线程对象,并且执行其他线程
setPriority:设置优先级:所有线程默认的优先级是5;
MAX_PRIORITY:线程可以具有最高的优先级
MIN_PRIORITY:线程可以具有最低的优先级
NORM_PRIORITY:分配给线程默认的优先级
例如:
public class JoinThread {/** * @param args * @throws InterruptedException */public static void main(String[] args) throws InterruptedException {JoinDemo jd = new JoinDemo();Thread t1 = new Thread(jd);Thread t2 = new Thread(jd);t1.join();//抢夺cpu的执行权,主线程等待t1执行完后,才能继续执行。t1.setPriority(Thread.MAX_PRIORITY);//设置优先级t2.start();//t1.join();//主线程碰到了t1是释放执行权,t1和t2交替执行,直到t1完成后主线程复活,不管t2是否结束。for(int x = 0;x<80;x++){System.out.println("main..."+x);}System.out.println("over");}}class JoinDemo implements Runnable{public void run() {for(int x = 0;x<10;x++){System.out.println(Thread.currentThread().getName()+"..."+x);Thread.yield();//暂停当前的线程执行其他的线程}}}开发中线程的书写:
class ThreadTest {public static void main(String[] args) {new Thread(){public void run(){for(int x = 0;x<100;x++){System.out.println(Thread.currentThread().getName()+"..."+x);}}}.start();for(int x = 0;x<100;x++){System.out.println(Thread.currentThread().getName()+"..."+x);}Runnable r = new Runnable(){public void run(){for(int x = 0;x<100;x++){System.out.println(Thread.currentThread().getName()+"..."+x);}}};new Thread(r).start();//new Test1().start();}}/*class Test1 extends Thread{public void run(){for(int x = 0;x<100;x++){System.out.println(Thread.currentThread().getName()+"..."+x);}}}*/
0 0
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- 线程
- js闭包理解
- C++Primer + 12章
- JavaScript总结--创建对象
- 职业程序员的克制与放纵
- C++ delete指针依然可访问问题
- 线程
- poj2528(线段树离散染色)
- ubuntu12.04 禁用访客
- C语言编程:从键盘中输入一个数字,返回值为整数有效位数
- A Tour of Go Named results
- POJ 3304 segments (直线与线段相交)
- 忆一道百度面试题
- struct和typedef struct的区别
- leetcode - Find Minimum in Rotated Sorted Array II