一.继承Thread类重写run()方法
编写一个类继承Thread,重写run()方法
public class SubThread extends Thread{
@Override
public void run() {
//获取当前线程,执行当前代码的线程
Thread t = Thread.currentThread();
//获取线程的名称
String name = t.getName();
//System.out.println(name+" hello Thread");
for (int i = 0; i < 100; i++) {
System.out.println(name+"--"+i);
try {
Thread.sleep(100);//单位是毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
调用线程对象的start()方法启动线程
public static void main(String[] args) {
//创建一个子线程
Thread t = new SubThread();
t.start();
//如果直接调用run方法,run方法中的代码是由主线程执行的
//t.run();
}
二.使用匿名内部类的方式创建Thread类型的对象
public static void main(String[] args) {
//创建线程对象
Thread t = new SubThread();
t.setName("a");
t.start();
//使用匿名内部类的方式创建Thread类型的对象
Thread t2 = new Thread("b") {
@Override
public void run() {
//获取线程的名称
String name = Thread.currentThread().getName();
//定义变量,保存总和
int sum = 0;
for (int i = 1; i <= 200; i++) {
sum+=i;
}
System.out.println(name+":"+sum);
}
};
//启动线程
t2.start();
}
三.实现Runnable接口,重写run()方法
Runnable接口的方式和继承Thread类的方式:建议使用Runnable方式
a.线程和任务分离,解耦合,提高代码的健壮性。
b.避免了Java单继承的局限性
c.线程池里面,只能传入Runnable或者Callable类型的对象,不用new Thread
Runnable接口:
public abstract void run();
public static void main(String[] args) {
//创建一个Runnable类型的对象
Runnable r = new Runnable() {
@Override
public void run() {
String name = Thread.currentThread().getName();
System.out.println(name);
}
};
//把Runnable类型的对象,赋值给Thread类的成员变量target
Thread t = new Thread(r,"线程1");
t.start();
}
public static void main(String[] args) {
//创建一个Runnable类型的对象
Runnable r = new Runnable() {
@Override
public void run() {
//获取线程的名称
String name = Thread.currentThread().getName();
for (int i = 1; i <= 100; i++) {
System.out.println(name+"--"+i);
}
}
};
new Thread(r).start();
new Thread(r).start();
}
四. 实现Callable接口,重写call()方法
Runnable是执行工作的独立任务,但是不返回任何值。如果我们希望任务完成之后有返回值,可以实现Callable接口
该方法效率较低,因为在获取线程的执行结果的时候,当前线程受阻塞。但是可以拿到线程的返回结果
/*
* 实现Callable接口,重写call方法,利用FutureTask可以获取线程返回值
*
*/
public class CallableTest implements Callable {
@Override
public Object call() {
System.out.println("run===" + Thread.currentThread().getId());
return "success" + Thread.currentThread().getId();
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableTest callableTest = new CallableTest();
for (int i = 0; i < 10; i++) {
FutureTask<Object> future = new FutureTask<Object>(callableTest);
Thread thread = new Thread(future);
thread.start();
// 获取返回值
future.get();
}
}
}
线程池:
1.提高响应速度。预先创建好了线程,只等任务过来执行。
2.降低资源消耗。线程池中的线程,执行完任务后,又返回到线程池中,下一个任务到来后可以继续使用该线程。
3.提高线程的可管理性。一个线程大约需要消耗1M的空间,线程池可以设置最大线程的数量。
Executors类:
static ExecutorService newFiexdThredadPool(int nThread);
ExecutorService接口:
void execute(Runnable r);//执行任务
<T> Future<T> submit(Callable<T> c);
Future<?> submit(Runnable r);
void shutdown();//关闭线程池
注意线程安全:
线程安全产生的前提:多个线程访问同一资源(数据)
整个的操作不是原子操作:
1.使用synchronized代码块
语法:
synchronized(锁对象){//可以是任意类型的对象
//写有可能发生线程安全问题的代码
}
2.使用synchronized方法
静态synchronized方法:在static和返回值之间加synchronized关键字
非静态synchronized方法:在返回值之前加synchronized关键字
synchronized代码块和方法:代码执行完毕后,自动释放锁
3.使用Lock锁
Lock接口:
void lock();//上锁
void unlock();//开锁
常用实现类:ReentrantLock
构造方法:
public ReentrantLock();
要想达到线程安全的效果,必须是同一个锁对象
synchronized方法:
静态:当前类的反射对象,获取当前类的反射对象
Class c = 类名.class;//获取一个类的反射对象
非静态:锁对象是this
Java中线程的状态:
NEW:新建状态。创建了一个线程,启动之前处于该状态
RUNNABLE:可运行状态。线程正在执行任务(run方法中的代码),就处于该状态
BLOCKED:阻塞状态。获取synchronized锁对象失败,就处于该状态
WAITING:无限等待状态。获取Lock锁对象失败,就处于该状态
TIMED_WAITING:计时等待状态。线程正在执行sleep方法的时候,就处于该状态
TERMINATED:消亡状态。线程执行完任务后,就处于该状态