多线程的创建
方式一:继承Thread类(不能返回线程执行结果)
——1.定义一个线程类继承Thread类
——2.重写run方法,定义线程要干啥
——3.new 一个线程对象
——4.调用start方法启动线程(执行run方法)
实例:
package DemoCreate;
/*
//1.定义一个线程类继承Thread类
*/
public class MyThread extends Thread
{
//2.重写run方法,定义线程要干啥
@Override
public void run() {
for (int i = 0 ; i< 100;i++){
System.out.println("子线程执行输出"+i);
}
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
//3.new 一个线程对象
Thread t = new MyThread();
/*
4.调用start方法启动线程(执行run方法)
这里不能直接调用run,否则会被当成普通方法执行,相当于单线程
*/
t.start();
for (int i = 0;i<100;i++){
System.out.println("主线程执行输出"+i);
}
}
}
方式二:实现Runnable接口(不能返回线程执行结果)
——1.定义一个线程任务类,实现Runnable接口
——2.重写run方法,定义线程的执行任务
——3.创建一个任务对象(实现Runnable接口)
——4.将任务对象交给Thread处理
——5.启动线程
实例:
public class MyRunnable implements Runnable{
/*
2.重写run方法,定义线程的执行任务
*/
@Override
public void run() {
for (int i = 0;i < 100;i++){
try {
Thread.sleep(200);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"子线程执行输出"+i);
}
}
}
public class ThreadDemo2 {
public static void main(String[] args) {
//3.创建一个任务对象
Runnable r = new MyRunnable();
//4.将任务对象交给Thread处理
Thread t = new Thread(r,"hja");
//5.启动线程
t.start();
try {
t.join(200);
} catch (Exception e) {
e.printStackTrace();
}
for (int i = 0 ; i<100 ; i++){
System.out.println("主线程执行输出"+i);
}
}
}
方式三:利用Callable、FutureTask接口实现
——1.定义一个任务类 实现Callable接口(因该声明线程任务执行完毕后的结果的数据类型)
——2.重写cal方法(线程的任务方法)
——3.创建Callable任务对象
——4.把Callable(不能直接交给Thread)任务对象 交给FutureTask (实现的是Runnable接口可以交给Thread)对象
——5.交给线程处理
——6.启动线程
实例:
public class ThreadDemo3 {
public static void main(String[] args) {
/*
3.创建Callable任务对象
*/
Callable<String> call = new MyCallable(100);
//4.把Callable任务对象 交给FutureTask 对象
// FutureTask对象 相当于 Runnable对象(实现Runnable接口) 可以交给Thread
// FutureTask对象 可以在线程结束之后通过调用get方法得到线程执行后结果
FutureTask<String> f = new FutureTask<>(call);
//5.交给线程处理
Thread t = new Thread(f);
//6.启动线程
t.start();
Callable<String> call1 = new MyCallable(200);
FutureTask<String> f1 = new FutureTask<>(call1);
Thread t1 = new Thread(f1);
t1.start();
try {
String rs = f.get();
System.out.println("第一个结果:"+rs);
} catch (Exception e) {
e.printStackTrace();
}
try {
String rs1 = f1.get();
System.out.println("第二个结果:"+rs1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class MyCallable implements Callable <String>{
private int n ;
public MyCallable(int n) {
this.n = n;
}
/*
2.重写cal方法(线程的任务方法)
*/
@Override
public String call() throws Exception {
int sum = 0;
for (int i = 0; i <= n ; i++){
sum +=i;
}
return "子线程执行的结果是:"+sum;
}
}
线程的状态转换
线程状态相关方法
——设置线程名称
Thread t = new Thread(线程名);
Thread t = new Thread(实现Runnable接口类的对象, 线程名称);
Thread t = new Thread(); t.setName(线程名称);
——获取线程名称
Thread子类中 getNameString name = Thread.currentThread().getName();
——判断线程是否启动(是否调用start)
isAlive
——线程合并
join
——线程的暂停
yield
——线程的(睡眠)暂停
sleep
线程同步
线程安全问题
——竞态条件当多个线程读写共享资源时,就称存在竞态条件;
——一段代码如果对共享资源的多线程读写操作,这段代码就被称为临界区;
解决方式:
——加synchronized锁
——加Lock锁
售票案例引入:
package DemoCreate;
/*
多线程的创建方式二:实现Runnable接口
*/
public class SellTicket implements Runnable{
private int ticket = 100;
//定义锁住的对象
方式一:private Object object = new Object();
方式二:Lock lock = new ReentrantLock();
public static void main(String[] args) {
//3.创建一个任务对象
SellTicket r = new SellTicket();
//4.将任务对象交给Thread处理
Thread t = new Thread(r,"窗口一");
Thread t1 = new Thread(r,"窗口二");
Thread t2 = new Thread(r,"窗口三");
//5.启动线程
t.start();
t1.start();
t2.start();
}
//锁方法
public synchronized void SellTicket(){
if (ticket > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在出售" + ticket + "张票");
ticket--;
}
}
@Override
public void run() {
//线程不安全
while (true) {
//解决方式一:synchronized 锁住的是一个对象 锁代码块
synchronized (object) {
if (ticket > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在出售" + ticket + "张票");
ticket--;
}
}
//解决方式一:synchronized 锁方法体
while (true) {
this.SellTicket();
}
//解决方式二:加Lock锁
try {
lock.lock();//上锁
if (ticket > 0) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("正在出售" + ticket + "张票");
ticket--;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();//开锁
}
}
}
}
Lock&synchronized
Lock | synchronized |
---|---|
显式锁(手动开启和关闭锁) | 隐式锁,出了作用域自动释放 |
只有代码块锁 | 有代码块锁和方法锁 |
性能更好,并且具有更好的扩展性 |
优先使用顺序:Lock > synchronized块 > synchronized方法;
死锁问题
死锁发生的四个必要条件:
——互斥使用
——不可抢占
——请求和保持
——循环等待