您的位置:首页 > 其它

多线程基础知识第一篇:创建线程的方式及线程常用的方法

2015-07-08 17:30 561 查看
首先说一下为什么要使用多线程。

线程是大多数操作系统调度的基本单元,一个程序作为一个进程来执行,程序运行过程中能够创建多个线程,而一个线程在一个时刻只能运行在一个处理器核心上。也就是说单线程程序只能使用一个处理器核心,那么加入再多的处理器核心也无法显著提升程序的执行效率。相反,如果该程序使用多线程技术,将计算逻辑分配到多个处理器核心上,就会显著减少程序的处理时间,并且随着更多处理器核心的加入而变得更有效率。

线程的创建,有3种方式:继承Thread类、实现Runnable接口、实现Callable接口

1.继承Thread类的方式:

class SubThread extends Thread {
private int count;

public void run() {
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("current thread: " + Thread.currentThread().getName());
}
}

public class ThreadTest {
public static void main(String[] args) {
Thread thread1 = new SubThread();
thread1.setName("线程1");
thread1.start();
}
}


以上代码,用Thread子类的构造器产生一个Thread子类对象并向上转型为Thread类型。之后可以给线程对象设置名字、设置优先级,然后调用start()方法启动线程。如果不给线程设置名字,则线程的名字默认为Thread-0、Thread-1这种格式的。如果不给线程设置优先级,则线程的优先级默认是5,即是Thread.NORM_PRIORITY。

如果想在创建线程的时候就设置名字,则可创建一个String类型的成员变量,然后调用以此变量为参数的构造器就可得到一个线程对象,值得注意的是,必须要在此构造器中调用父类也就是Thread的参数是String类型的构造器:

class SubThread extends Thread {
private int count;
private String name;

public SubThread(String name) {
super(name);
this.name = name;
}

public SubThread() {
super();
}

public void run() {
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("current thread: " + Thread.currentThread().getName());
}
}

public class ThreadTest {
public static void main(String[] args) {
Thread thread1 = new SubThread("线程1");
thread1.start();
}
}


之前一直以为,继承Thread类创建线程的方式,多个线程不能共享此线程类的成员变量。其实这是不对的。如果这个成员变量是一般类型(基本类型、String类型、Date类型、集合类型),这种说法是成立的,毕竟每一次调用new SubThread()方法就创建了一个新的对象,这些个成员变量都是不同对象的成员变量。但是如果这个成员变量是自定义的类类型,而且线程对象是调用以此自定义类类型变量为参数的构造器生成的,则此时多个线程可以共享Thread子类的成员变量,毕竟调用有参构造器时传入的参数是同一个:

class Account {
private int count = 500;

public void dec() {
while (count > 1) {
synchronized (this) {
count = count - 1;
System.out.println("current thread: " + Thread.currentThread().getName() + ",count=" + count);
}
}
}
}

class SubThread extends Thread {
private String name;
private Account account;

public SubThread(String name, Account account) {
super(name);
this.name = name;
this.account = account;
}

public SubThread() {
super();
}

public void run() {
account.dec();
}
}

public class ThreadTest {
public static void main(String[] args) {
Account account = new Account();
Thread thread1 = new SubThread("线程1", account);
Thread thread2 = new SubThread("线程2", account);
thread1.start();
thread2.start();
}
}


如上,调用第三方类变量作为参数的构造器来创建线程,当传入同一个第三方类实例时,创建的多个线程就可以共享这个第三方类实例,当然也就共享这个第三方类实例的成员变量了。

2.实现Runnable接口的方式:

class ThreadImpl implements Runnable {
private int count;

@Override
public void run() {
while (true) {
count = count + 1;
System.out.println("count=" + count);
if (count > 100) {
break;
}
}
}
}

public class ThreadTest {
public static void main(String[] args) {
Runnable target = new ThreadImpl();
Thread thread1 = new Thread(target);
thread1.start();
}
}


以上,其实是用了Thread(Runnable target) 构造器,传入一个Runnable对象就可以得到一个线程对象。如果想在创建线程的时候给线程设置名字,则可以用另一个构造器Thread(Runnable target, String name) ,第二个参数传线程名字就好了。如果创建N个线程传的是同一个Runnable对象,则这N个线程共享此Runnable对象的成员变量:

class ThreadImpl implements Runnable {
private int count;

@Override
public void run() {
while (true) {
count = count + 1;
System.out.println("当前线程:" + Thread.currentThread().getName() + ",count:" + count);
if (count > 100) {
break;
}
}
}
}

public class ThreadTest {
public static void main(String[] args) {
Runnable target = new ThreadImpl();
Thread thread1 = new Thread(target, "线程1");
Thread thread2 = new Thread(target, "线程2");
thread1.start();
thread2.start();
}
}


以上,用两个线程对同一个变量account进行增长操作,在打印结果中线程1和线程2交叉出现,顺序不一定,account的值从1一直升到101,中间顺序可能紊乱,也能有几条相同,这就是线程不安全。

线程的优先级:

线程的优先级是用1到10的整数表示的,数值越大,表示优先级越高,获得时间片从而被CPU执行的机会越大。

Thread类有3个final的静态变量,Thread.MIN_PRIORITY、Thread.NORM_PRIORITY、Thread.MAX_PRIORITY,值分别是1、5、10,分别对应着线程的最低优先级、普通优先级和最大优先级。默认优先级是NORM_PRIORITY,即每个线程在new出来之后的优先级都是NORM_PRIORITY,可以调用getPriority()/setPriority(int newPriority)对象方法得到或是设置线程的优先级。

Thread类常用的静态方法:

1.Thread.activeCount(),得到存活的线程数,返回值是int类型;

2.Thread.currentThread(),得到当前线程,返回值是个Thread对象,一般后面再接着调用getName()方法,得到当前线程的名字;

3.Thread.sleep(long millis),让当前线程睡眠指定时间,以毫秒为单位;

4.Thread.yield(),让当前线程释放掉调度器的调度,使得调度器重新调度。一般来说只有优先级比当前线程高或者至少跟当前线程优先级相同的线程才可能得到调度器的调用,否则还是该线程被调度器调用。

Thread类常用的非静态方法:

1.thread1.get/setName(),thread1.get/setPriority(),thread1.isAlive() 是否存活,thread1.isDaemon() 是否是后台线程,thread1.getState() 得到线程的状态,getTheadGroup() 得到线程组。在Thread类中有一个内部枚举类State,里面有6个值,对应线程的6种状态:NEW、RUNNABLE 、BLOCKED、WAITING、TIMED_WAITING、TERMINATED,可以看源码的注释来理解各个状态。

2.thread1.join():当前线程阻塞,等待thread1线程执行后再执行。

3.wait() 使当前线程等待,notify() 唤醒在此同步监视器上等待的单个线程,notifyAll() 唤醒在此同步监视器上等待的所有线程:这三个方法不是Thread类定义的,而是继承Object类得到的,这三个方法只能由同步监视器对象调用。synchronized同步方法,this就是同步监视器对象。synchronized同步代码块,synchronized后面括号里的对象就是同步监视器对象。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: