Java线程(一)
1.线程概述
进程是计算机特定的在数据集上的一次运行。
线程是指进程中的一个执行流程,一个进程中可以运行多个线程。比如java.exe进程中可以运行很多线程。线程总是属于某个进程,进程中的多个线程共享进程的内存。
二,线程的创建方式
第一种 继承Thread类,重写该类的run()方法。
public class MyThread extends Thread {
@Override
public void run() {
// 线程执行体
for(int i=0;i<10;i++) {
System.out.println(this.getName()+"运行"+i);
}
}
}
public static void main(String[] args) {
MyThread mt=new MyThread();//创建第一个线程
MyThread mt1=new MyThread();//创建第二个线程
mt.start();//第一个线程准备就绪
mt1.start();//第二个线程准备就绪
}
如上所示,继承Thread类,通过重写run()方法定义了一个新的线程类MyThread,其中run()方法的方法体代表了线程需要完成的任务,称之为线程执行体。当创建此线程类对象时一个新的线程得以创建并通过调用线程对象的start()方法,使得该线程进入到准备就绪状态,此时该线程并不一定会马上得以执行,这取决于CPU是否有资源空余。
第二种 实现Runnable()的类,该类之后实现run()方法。然后可以分配该类的实例,在创建Thread作为一个来传递并启动。
public class MyRunnable implements Runnable {
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println(Thread.currentThread().getName()+"-运行"+i);
}
}
}
public static void main(String[] args) {
//创建两个线程
MyRunnable mr=new MyRunnable();
Thread t1=new Thread(mr);
MyRunnable mr1=new MyRunnable();
Thread t2=new Thread(mr1);
t1.setName("线程1");//设置线程名
t2.setName("线程2");
t1.start();
t2.start();
}
第三种 使用Callable和Future接口创建线程
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() {
int sum = 0;
for (int i=0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
public static void main(String[] args) {
Callable<Integer> myCallable = new MyCallable(); // 创建MyCallable对象
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask来包装MyCallable对象
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 30) {
Thread th = new Thread(ft); //FutureTask对象作为Thread对象的target创建新的线程
th.start(); //线程进入到就绪状态
}
}
System.out.println("主线程for循环执行完毕..");
try {
int sum = ft.get(); //取得新创建的新线程中的call()方法返回的结果
System.out.println("sum = " + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
由此看出,在实现Callable接口中,不再是run()方法了,而是call()方法作为线程执行体,同时还具有返回值!在创建新的线程时,是通过FutureTask来包装MyCallable对象。
上述主要讲解了三种常见的线程创建方式,线程的启动,都是调用线程对象的start()方法,需要特别注意的是:不能对同一线程对象两次调用start()方法。
三 线程的执行原理以及生命周期
1.执行原理
线程的并发执行通过多个线程不断切换cpu的资源,速度非常快,以至于我们感觉是同步执行的。
2.生命周期
创建:一个或者多个线程被创建出来。
准备就绪:线程具有执行资格,即调用了start()方法,但是没有执行的权利。
运行:CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行。即具备执行资格和执行权利。
阻塞:处于运行状态中的线程由于某种原因,暂时放弃对CPU的使用权,停止执行。即没有执行资格和执行权利。
1.等待阻塞:运行状态中的线程执行wait()方法,使本线程进入到等待阻塞状态;
2.同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态;
3.其他阻塞 – 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
销毁:线程资源变成垃圾,需要释放资源,该线程结束生命周期。
四 并发安全
针对并发安全性问题,我们需要使用同步锁语法(一个资源只能由一个人访问)
synchronized(锁对象){
//共享资源代码
}
同步代码适用范围:
1.代码中有多个线程
2.代码中有共享数据
3.共享数据被多条语句操作
synchronized 同步锁
private static int tickers=100;
//定义同步锁
private static Object ojb=new Object();
public void run() {
// TODO Auto-generated method stub
while(true) {
//同步代码块
synchronized (ojb) {
if(tickers>0) {
System.out.println(this.getName()+"正在售卖第"+tickers+"张票");
tickers--;
}else {
System.out.println("票已经售完了!!!!!!!");
break;
}
}
}
}
public static void main(String[] args) {
MyThread mt1=new MyThread();
MyThread mt2=new MyThread();
MyThread mt3=new MyThread();
MyThread mt4=new MyThread();
mt1.setName("窗口1");
mt2.setName("窗口2");
mt3.setName("窗口3");
mt4.setName("窗口4");
mt1.start();
mt2.start();
mt3.start();
mt4.start();
}
synchronized 同步代码块的对象是任意对象(注意:仅是实现方式是继承Thread)
这个对象必须是线程类共享的(静态对象)
synchronized 可以加在方法,如果是静态方法synchronized 的锁的对象类的类对象。
如果不是静态方法,synchronized如果对象方法,他的锁是this本身的对象。