进程: 一个程序开启的时候,会启动一个进程,在进程中会开启线程,如果只有一个线程,那么这个线程就称为主线程。如果进程停止了,那么会干掉线程再推出。 在windows系统上,如果启动一个应用之后推出,但是你在管理器上还能看见这个应用的.exe还在,原因就是里面还有线程在执行任务,把这个应用的.exe干掉,那么就会先干掉里面的线程,再推出。 java程序中,我们执行程序的时候,会开启java 的JVM,即启动java JVM的时候会有一个进程:java.exe。在这个进程中至少启动一个线程去执行main函数。 线程:就是程序内部的一条执行线索,在程序中,线程的任务就是去执行任务,比如下载获取数据等
线程 是程序中的执行线程。Java 虚拟机允许应用程序并发地运行多个执行线程。
每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。每个线程都可以或不可以标记为一个守护程序。当某个线程中运行的代码创建一个新
Thread
对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。当 Java 虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的
main
方法)。Java 虚拟机会继续执行线程,直到下列任一情况出现时为止:
- 调用了
Runtime
类的exit
方法,并且安全管理器允许退出操作发生。 - 非守护线程的所有线程都已停止运行,无论是通过从对 run 方法的调用中返回,还是通过抛出一个传播到
run
方法之外的异常。
线程创建的两种方式:
package com.enterise.test.thread; public class CreateThread { public static void main(String[] args) { ThreadOne threadOne = new ThreadOne(); threadOne.start(); new Thread(new RunnableOne()).start(); } } /** * 线程创建方式一: * 继承Thread * @author Always * */ class ThreadOne extends Thread { @Override public void run() { super.run(); while (true) { try { this.sleep(500); System.out.println("thread :" + this.getName()); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * Thread创建方式二: * 实现Runnable * @author Always * */ class RunnableOne implements Runnable { @Override public void run() { while (true) { try { new Thread().sleep(500); System.out.println("thread :" + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } } }
继承Thread的线程: 当启动这个线程的时候,会调用这个线程的run方法,其实不然,当我们这个run方法是覆盖父类的run方法的时候:
----------------------------/** * If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ public void run() { if (target != null) { target.run(); } }
/* What will be run. */ private Runnable target;
----------------------------
也就是说当继承thread的run方法覆盖父类,那么会执行父类的run方法,在父类中是去判断target是否为空,这个target是runnable接口,如果这个target不为空,那么就会去执行runnable中的run方法,而不是去执行thread本身的run方法。实验:
package com.enterise.test.thread; public class TestThread { public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { while(true){ try { new Thread().sleep(500); System.out.println("--runnable->"+Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } } }){ @Override public void run(){ // super.run(); while(true){ try { new Thread().sleep(500); System.out.println("--thread.run->"+Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } } }.start(); } }
结果:
执行runnable中的run代码。
如果有
super.run();
执行thread中的run代码。
实现方式和继承方式的区别:
实现方式和继承方式的区别: 实现:避免了单继承的局限性。 继承:thread中的run。 实现:runnable中的run。
线程的两个方法(run start): start() 使该线程开始执行;Java 虚拟机调用该线程的 run 方法。 run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。
package com.enterise.test.thread; public class ThreadTwo { public static void main(String[] args) { MethodThread thread = new MethodThread(); thread.start(); // thread.run(); System.out.println("------main-thread-------"); } } class MethodThread extends Thread { private int count = 5; private boolean tag = true; public void run() { while(tag){ try { Thread.sleep(500); if(count > 0){ count --; System.out.println("--->"+Thread.currentThread().getName()); }else { tag = false; } } catch (InterruptedException e) { e.printStackTrace(); } } } }
执行结果: start: ------main-thread------- --->Thread-0 --->Thread-0 --->Thread-0 --->Thread-0 --->Thread-0 run: --->main --->main --->main --->main --->main ------main-thread------- 即: 调用start方法是启动线程调用run方法。 调用run方法是创建了线程,执行run方法,没有开启线程,也就是执行一个类调用里面的方法而已。
线程的几个状态: start();启动线程执行run方法。 sleep();线程执行时,遇到sleep,者该线程执行睡眠,当睡眠时间达到的时候,会自动在执行下去。 wait();等待,使该线程等待着,进入线程池中。 notify();唤醒线程。(线程池:先进先出,唤醒先进去的线程)唤醒一个线程。 notifyAll();唤醒所有线程(线程池中的所有线程) stop();停止线程。 run();方法执行结束。消亡。 运行状态:持有cpu的执行权。 临时状态(阻塞):没有cpu执行权(等待cpu的执行权)。 冻结状态:放弃了执行资格。(wait、sleep)
线程安全问题: 就是多线程在操作同一块代码时对共享数据进行操作,导致数据不合理的情况出现。 线程安全问题都是由全局变量及静态变量引起的。 即线程操作的是每个线程共享的数据。 若每个线程中对全局变量、静态变量只有读操作,而无写操作,一般来说,这个全局变量是线程安全的; 若有多个线程同时执行写操作,一般都需要考虑线程同步,否则的话就可能影响线程安全。 即:引发线程出现安全的前提: 1.两个或者两个以上的线程。 2.操作的数据是几个线程所共享的。 run方法中: if(count > 0){ count --; System.out.println("--->"+Thread.currentThread().getName()); }else { tag = false; } count:是全局变量。 当第一个线程进入判断的代码时,比如是count=1,在没有进行操作--之前,cpu的执行权转到了另外一个线程上, 这个时候count还是1,进入判断操作--,此时count=0,然后打印,之后另外一个线程抢到cpu的执行权,执行--操作,打印的数据就成为了-1。这样导致数据的不合理性。 问题的根据就是在操作数据的时候几个线程同步进行着,解决问题的办法就是在操作数据的时候只让一条线程执行,等执行完之后,另外一条线程再去执行数据的操作,这样就避免了数据操作的错乱。
package com.enterise.test.thread; public class ThreadTwo { public static void main(String[] args) { Ticket ticket = new Ticket(); //开启两条线程执行卖票 new Thread(new SellTicketThread(ticket)).start(); new Thread(new SellTicketThread(ticket)).start(); } } class SellTicketThread implements Runnable { private Ticket ticket; public SellTicketThread(Ticket ticket){ this.ticket = ticket; } @Override public void run() { while(ticket.isok){ try { Thread.sleep(50); //卖票 ticket.sellTicket(); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Ticket { private int ticketNum = 100; public boolean isok = true; public void sellTicket(){ if(ticketNum > 0){ ticketNum--; System.out.println("ticiketNum-->"+ticketNum); }else { isok = false; } } } /** * 执行结果: * ticiketNum-->10 ticiketNum-->9 ticiketNum-->9 ticiketNum-->8 ticiketNum-->7 * 出现了两个9 9 * */
同步代码块和同步函数:synchronized
在出现操作共享数据的代码块上加同步代码块:
synchronized (this) { if(ticketNum > 1){ ticketNum--; System.out.println("ticiketNum-->"+ticketNum); }else { isok = false; } }
1线程进入同步代码块之前会判断这个锁是否是开的,如果是开的者进去,之后把锁关掉,然后执行数据,其他的线程抢到了cpu的执行权,到锁之前判断锁如果锁是关的者是进不去就在此等候,里面的线程操作数据完毕之后,会把锁打开,其他的线程按照之前的步骤同样执行。
this代表的是锁,只要是任意的对象就可以,如果锁不一致,那么数据还是错乱的原因就是每个线程有各自的锁。
同步函数:public synchronized void sellTicket(){ if(ticketNum > 1){ ticketNum--; System.out.println("ticiketNum-->"+ticketNum); }else { isok = false; } }
当同步函数是静态的时候:class Ticket { private static int ticketNum = 100; public static boolean isok = true; public static synchronized void sellTicket(){ if(ticketNum > 1){ ticketNum--; System.out.println("ticiketNum-->"+ticketNum); }else { isok = false; } } }
结果其实还是错乱的:原因是synchronized的锁是this,但是静态函数在jvm加载代码的时候就为这个函数开辟了空间,这个时候还没有这个对象也就是没有this这个概念。
所以static的同步函数的锁是类.class
class Ticket { private static int ticketNum = 100; public static boolean isok = true; public static void sellTicket(){ synchronized(Ticket.class){ if(ticketNum > 1){ ticketNum--; System.out.println("ticiketNum-->"+ticketNum); }else { isok = false; } } } }
daixu----