什么是线程?
编辑搜图
如图,我们先介绍一下进程,进程只电脑里执行的每个软件,例如QQ\微信等程序都统一称之为进程,因为线程是在进程里执行的,一个进程里有多个线程。
进程想要执行任务就需要依赖线程。换句话说,就是进程中的最小执行单位就是线程,并且一个进程中至少有一个线程。
创建线程的三种方式
public class ThreadDemo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
new MyThread().start();//简化写法
MyThread2 myThread2 = new MyThread2();
new Thread(myThread2).start();
new Thread(new MyThread2()).start();//简化写法
FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3());//加个泛型约束Integer
new Thread(futureTask).start();
}
}
//继承 Thread 类
//Thread.currentThread().getName()获取当前线程的名字
class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"我是第一种方式");
}
}
//实现 Runnable 接口
class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"我是第二种方式");
}
}
//实现 Callable 接口
class MyThread3 implements Callable {
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"我是第三种方式");
return 666;
}
}
运行结果:
线程本身是不安全的
通过代码实现线程不安全的情况:例如火车站买票,20张票3个人一起买
public class Ticket {
public static void main(String[] args) {
MyTicket myTicket = new MyTicket();
new Thread(myTicket,"小明").start();
new Thread(myTicket,"小红").start();
new Thread(myTicket,"小亮").start();
}
}
//定义一个内部类
class MyTicket implements Runnable {
private int ticket = 20; //设置票数 20张票
boolean flag =true; //默认为true
@Override
public void run() {
while(flag){
try {
stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//定义一个方法 停止抢票
public void stop() throws InterruptedException {
if (ticket<=0){
flag = false;//如果票数为0或小于0 停止抢票
return;
}
//为了实现不安全的结果 让线程等待
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"买到了第"+ticket-- +"张票");
}
}
编辑搜图
运行结果:小红和小亮都抢到了第10张票。这个在实际中肯定是不允许的
如何确保线程安全?
-
synchronized
-
Lock
synchronized关键字,就是用来控制线程同步的,保证我们的线程在多线程环境下,不被多个线程同时执行,确保我们数据的完整性,使用方法一般是加在方法上。
//定义一个内部类
class MyTicket implements Runnable {
private int ticket = 20; //设置票数 20张票
boolean flag =true; //默认为true
@Override
public synchronized void run() {
while(flag){
try {
stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
编辑搜图
这样就可以确保我们的线程同步了,同时这里需要注意一个大家平时忽略的问题,首先synchronized锁的是括号里的对象,而不是代码,其次,对于非静态的synchronized方法,锁的是对象本身也就是this。
当synchronized锁住一个对象之后,别的线程如果想要获取锁对象,那么就必须等这个线程执行完释放锁对象之后才可以,否则一直处于等待状态。
注意点:虽然加synchronized关键字,可以让我们的线程变得安全,但是我们在用的时候,也要注意缩小synchronized的使用范围,如果随意使用时很影响程序的性能,别的对象想拿到锁,结果你没用锁还一直把锁占用,这样就有点浪费资源。
Lock的用法如下图:
public class LockTest {
public static void main(String[] args) {
MyLock myLock =new MyLock();
new Thread(myLock,"小明").start();
new Thread(myLock,"小红").start();
new Thread(myLock,"小亮").start();
}
}
class MyLock implements Runnable{
int piaoNum = 10;
boolean flag =true;
private final ReentrantLock lock = new ReentrantLock(); //定义一个锁
@Override
public void run() {
while(flag) {
lock.lock();//锁
if (piaoNum > 0) {
try {
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "购买了第" + piaoNum-- + "张票");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();//释放锁!!!
}
} else {
flag = false;
}
}
}
}
编辑搜图
注意一定要在finally中释放锁 以上就是使用Lock,来保证我们线程安全的方式