您的位置:首页 > 其它

Thread(线程),run(),start()

2016-06-20 16:02 190 查看
进程:
一个程序开启的时候,会启动一个进程,在进程中会开启线程,如果只有一个线程,那么这个线程就称为主线程。如果进程停止了,那么会干掉线程再推出。
在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代码。
如果有<pre class="java" name="code">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----
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: