现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程。比如在Windows系统中,一个运行的exe就是一个进程。线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。“同时”执行是人的感觉,在线程之间实际上轮换执行。
二.线程的创建与启动:
线程创建有两种方法:
1、扩展java.lang.Thread类。通过继承Thread类并重写run方法
2、实现java.lang.Runnable接口。实现Runnable接口并实现run方法
实例代码:
public class ThreadTest extends Thread {
String name;
public ThreadTest(String name){
super(name);
this.name=name;
}
@Override
public void run(){
for (int i = 0; i < Main.COUNT; i++) {
System.out.println("thread"+i);
}
}
}
class RunnableTest implements Runnable {
@Override
public void run() {
for (int i = 0; i <Main.COUNT; i++) {
System.out.println("Runnable!!"+i);
}
}
}
线程的实例化:
1、如果是扩展java.lang.Thread类的线程,则直接new即可。
2、如果是实现了java.lang.Runnable接口的类,则用Thread的构造方法:
Thread(Runnable target)
Thread(Runnable target, String name)
Thread(ThreadGroup group, Runnable target)
Thread(ThreadGroup group, Runnable target, String name)
Thread(ThreadGroup group, Runnable target, String name, long stackSize)
线程的启动:
在线程的Thread对象上调用start()方法,而不是run()或者别的方法。
代码实例:
public class Main {
public static final int COUNT=1000;
public static void main(String[] args) {
ThreadTest t1=new ThreadTest("我是线程1");
t1.start();
RunnableTest runnableTest=new RunnableTest();
Thread t2=new Thread(runnableTest);
t2.start();
}
}
1、新状态:线程对象已经创建,还没有在其上调用start()方法。
2、可运行状态:当线程有资格运行,但调度程序还没有把它选定为运行线程时线程所处的状态。当start()方法调用时,线程首先进入可运行状态。在线程运行之后或者从阻塞、等待或睡眠状态回来后,也返回到可运行状态。
3、运行状态:线程调度程序从可运行池中选择一个线程作为当前线程时线程所处的状态。这也是线程进入运行状态的唯一一种方式。
4、等待/阻塞/睡眠状态:这是线程有资格运行时它所处的状态。实际上这个三状态组合为一种,其共同点是:线程仍旧是活的,但是当前没有条件运行。换句话说,它是可运行的,但是如果某件事件出现,他可能返回到可运行状态。
5、死亡态:当线程的run()方法完成时就认为它死去。这个线程对象也许是活的,但是,它已经不是一个单独执行的线程。线程一旦死亡,就不能复生。 如果在一个死去的线程上调用start()方法,会抛出java.lang.IllegalThreadStateException异常。
四.线程常用方法:
1.isAlive(),线程是否还在运行.
2.getPriority(),获取当前线程优先级
3.setPriority(),线程优先级设置,值在10-1之间。注意优先级设置的越高不代表就一定先执行,只是被执行的可能性要大一些而已。
4.sleep(),休眠,指定的时间是(毫秒)或者(毫秒,纳秒)静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。当线程睡眠时,它入睡在某个地方,在苏醒之前不会返回到可运行状态。当睡眠时间到期,则返回到可运行状态。
5.join(),等待指定线程执行完毕。(相当于与执行线程合并)
6.yield(),让出cpu。让出一次也可能下次还是当前这个线程被执行。
五.线程同步:
线程的同步是保证多线程安全访问竞争资源的一种手段。一般用于当多个线程对同一个数据进行操作时。例如当几个人(几个线程)同时对一个账户里面去取钱时就会出现,每个人(每个线程)都会取到钱,而账户余额变负的情况,这肯定不合逻辑也不合情理,所以要解决这样的问题就要使用到线程同步。
线程同步的实现需要使用synchronized关键字,一是锁方法,而是锁住代码快。
实例代码:
public class SynTest {
public static void main(String[] args) {
//
Bank b = new Bank();
//
Thread t1 = new MThread("A", b);
Thread t2 = new MThread("B", b);
//
t1.start();
t2.start();
}
}
class Bank {
int money = 3000;
public void getMoney(String name, int money) {
synchronized (this) {
if (this.money >= money) {
//
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.money -= money;
System.out.println(name + "取出" + money + ",剩余:" + this.money);
} else {
System.out.println("余额不足!");
}
}
}
}
class MThread extends Thread {
Bank b;
MThread(String name, Bank b) {
super(name);
this.b = b;
}
@Override
public void run() {
b.getMoney(getName(), 2000);
}
}
这样就只能有一个线程能拿到钱,至于是谁先拿就看CPU先让谁执行了。
注意:
在使用synchronized关键字时候,应该尽可能避免在synchronized方法或synchronized块中使用sleep或者yield方法,因为synchronized程序块占有着对象锁,你休息那么其他的线程只能一边等着你醒来执行完了才能执行。不但严重影响效率,也不合逻辑。同样,在同步程序块内调用yeild方法让出CPU资源也没有意义,因为你占用着锁,其他互斥线程还是无法访问同步程序块。当然与同步程序块无关的线程可以获得更多的执行时间。
六.线程死锁问题
线程退出同步方法时将释放掉方法所属对象的锁,发生死锁的原因一般是两个对象的锁相互等待造成的。
实例代码:
public class SynTest {
//创建object对象
Object object1=new Object();
//创建object对象
Object object2=new Object();
public static void main(String[] args) {
//创建当前类的对象
SynTest synTest=new SynTest();
//启动线程1
new Thread(synTest.run1).start();
//启动线程2
new Thread(synTest.run2).start();
}
/**
* test1方法去拿test2方法
* */
public void test1() throws InterruptedException{
//锁定
synchronized (object1) {
System.out.println("test1拿test2");
//休眠
Thread.sleep(1000);
test2();
}
}
/**
* test2方法去拿test1方法
* */
public void test2() throws InterruptedException{
//
synchronized (object2) {
System.out.println("test2拿test1");
//
Thread.sleep(1000);
test1();
}
}
//匿名内部类是实现Runnable接口
Runnable run1= new Runnable() {
public void run() {
try {
test1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//匿名内部类是实现Runnable接口
Runnable run2= new Runnable() {
public void run() {
try {
test2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}