文章目录
- 一、多线程的入门了解
- 二、多线程的创建方式
- 1.继承Thread类
- 2.实现Runable接口
- 3.实现Callable接口
- 4.线程池创建
- 三、线程的生命周期
- 四、线程安全
- 1.synchronized
- 2.lock
一、多线程入门了解
程序:程序指的是为完成某种任务,用某种语言编写的一组指令的集合
线程:一个进程可以有多个线程,一个线程就是一个指令流
进程:进程指的是程序的一次执行过程
并发:一个 cpu 同时执行多个任务
并行:多个 cpu 同时执行多个任务
二、多线程的创建方式
1.继承Thread类
public class ThreadCreate1 {
public static void main(String[] args) {
//创建线程对象
MyThread1 myThread1 = new MyThread1();
myThread1.start();//用于开启一个线程,并且只能调用一次
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"打印"+i);
}
}
}
class MyThread1 extends Thread{
//run方法里面存放的是线程任务
@Override
public void run(){
for (int i = 0; i < 10; i++) {
//getName()用于获取线程的名称
System.out.println(getName()+"线程->"+i);
}
}
}
2.实现Runable接
public class RunableTest1 {
public static void main(String[] args) {
Thread thread = new Thread(new Gui(),"线程一");
Thread thread1 = new Thread(new Gui(),"线程二");
thread.start();
thread1.start();
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"->"+i);
}
}
}
class Gui implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"->"+i);
}
}
}
口
3.实现Callable接口
public class CallableTest1 {
public static void main(String[] args) {
Tq tq = new Tq();
//FutureTask可以用于接收Callable对象,还提供了一个get方法--用于拿去返回值
FutureTask futureTask = new FutureTask(tq);
FutureTask futureTask1 = new FutureTask(tq);
//由于Future Task实现Runable接口,对此他也是Runable的一个实例对象
Thread thread = new Thread(futureTask,"线程一");
Thread thread1 = new Thread(futureTask1,"线程二");
thread.start();
thread1.start();
}
}
class Tq implements Callable{
//Callable方法用于执行线程任务,并且可以i返回结果,还会抛出一个异常
@Override
public Object call() throws Exception {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName()+"->"+i);
}
return null;
}
}
4.线程池创建
public class Model5 {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new ThreadTest());
service.execute(new ThreadTest2());
service.shutdown();
}
}
class ThreadTest implements Runnable{
@Override
public void run() {
for (int i = 0; i < 30; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+"----->"+i);
}
}
}
}
class ThreadTest2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 30; i++) {
if (i % 2 != 0){
System.out.println(Thread.currentThread().getName()+"----->"+i);
}
}
}
}
三、线程的生命周期
新建:Thread类及其子类被声明
就绪:新建的线程调用了start()方法,进入线程队列等待CPU资源
运行:就绪状态的线程得到了CPU的资源,执行run()方法
阻塞:在某些特殊情况下,被人为挂起或执行输入输出操作的时候,线程对象让出
自己的 cpu 并终止任务进入阻塞状态
死亡:线程对象完成了它的线程任务或者被强制退出、发生异常等
四、线程安全
1.synchronized
如果我们要保证多线程的操作安全,那么我们可以通过 Synchronized 的同步代码块或
者方法从而保证线程的安全
①同步代码块
同步代码块就是通过 Synchronized 关键字将需要待同步的代码给放到指定代码块里面,
并使用同步锁去控制同步。格式:
synchronized(同步监视器){
代码
}
同步监视器:就是锁,任何一个对象都是锁,多个线程之间通信要使用同意一把锁
案例:
public class Model18 { public static void main(String[] args) { WindowSal sal = new WindowSal(); Thread thread1 = new Thread(sal,"窗口一"); Thread thread2 = new Thread(sal,"窗口二"); Thread thread3 = new Thread(sal,"窗口三"); thread1.start(); thread2.start(); thread3.start(); } } class WindowSal implements Runnable{ private int ticket = 1000; @Override public void run() { while (true){ synchronized (this){ if (ticket > 0){ System.out.println(Thread.currentThread().getName()+"正在售卖第 "+(100-ticket+1)+"张票"+"还剩下:"+(ticket-1)+"张票。"); ticket--; }else { break; } } } } }
②同步方法
同步方法就是用synchronized去修饰方法,把这个方法放到run方法里
public class Model18 { public static void main(String[] args) { WindowSal sal = new WindowSal(); Thread thread1 = new Thread(sal,"窗口一"); Thread thread2 = new Thread(sal,"窗口二"); Thread thread3 = new Thread(sal,"窗口三"); thread1.start(); thread2.start(); thread3.start(); } } class WindowSal implements Runnable{ private int ticket = 1000; @Override public void run() { while (true){ sal(); } } private synchronized void sal(){ if (ticket > 0){ System.out.println(Thread.currentThread().getName()+" 正在售卖第"+(1000-ticket+1)+"张票"+"还剩下:"+(ticket-1)+"张票。"); ticket--; }else { Thread.currentThread().stop(); } } }
③死锁
死锁:多个线程互相等待对方持有的锁,而在得到对方的锁之前都 不会释放自己的锁
案例:
class T1 extends Thread{ private static Object obj1 = new Object(); private static Object obj2 = new Object(); private int flag; T1(String name,int flag){ super(name); this.flag = flag; } @Override public void run() { if (flag == 1){ synchronized (obj1) { System.out.println(getName()+"已持锁一"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName()+"正在尝试获取锁二"); synchronized (obj2) { System.out.println(getName()+"已持锁二"); } } }else { synchronized (obj2) { System.out.println(getName()+"已持锁二"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName()+"正在尝试获取锁一"); synchronized (obj1) { System.out.println(getName()+"已持锁一"); } } } } } 我们可以看到上述代码运行以后线程一持有锁一并去尝试获取锁二,而线程二持有锁二 并尝试去获取锁一,结果就造成两个线程相互等待对方释放锁并去抢占然后两个线程就会一 直处于等待的过程中。 1、死锁产生的原因 线程对所分配到的锁具有排它性,一个线程获取到锁以后,其它线程只能等待该线程释 放锁,然后才能去抢占。 public class Model2 { public static void main(String[] args) { T1 t1 = new T1("线程一",1); T1 t2 = new T1("线程二",2); t2.start(); t1.start(); } }
2.lock
Lock 接口中规定的这些方法,分别可以用于加锁、释放锁等操作,具体如下所示:
⚫ void lock():获得锁。如果锁不可用,则当前线程将被禁用以进行线程调度,并处 于休眠状态,直到获取锁。
⚫ void lockInterruptibly():获取锁,如果可用并立即返回。如果锁不可用,那么当前 线程将被禁用以进行线程调度,并且处于休眠状态,和 lock()方法不同的是在锁的 获取中可以中断当前线程。 ⚫ boolean tryLock():只有在调用时才可以获得锁。如果可用,则获取锁,并立即返回 值为 true;如果锁不可用,则此方法将立即返回值为 false 。
⚫ boolean tryLock(long time, TimeUnit unit):超时获取锁,当前线程在一下三种情况 下会返回:1. 当前线程在超时时间内获得了锁;2.当前线程在超时时间内被中断; 3.超时时间结束,返回 false。
⚫ void unlock():释放锁。
我们在操作 Lock 锁需要注意的是 Lock 锁需要手动释放,如果发生了异常锁也不会被 释放,需要我们手动释放,对此我们在使用 Lock 锁时就需要将代码放在放在 try-finally 异 常处理机制中。
案例:
public class Model1 {
public static void main(String[] args) {
ThLock th1 = new ThLock("线程一");
ThLock th2 = new ThLock("线程二");
th1.start();
th2.start();
}
}
class ThLock extends Thread{
ThLock(String name){
super(name);
}
private static ReentrantLock lock1 = new ReentrantLock();
@Override
public void run() {
lock1.lock();
try{
if (lock1.isHeldByCurrentThread()){
System.out.println(getName()+"获取到锁资源 lock1");
}
sleep(1000);
}catch (Exception e){
e.printStackTrace();
} finally {
System.out.println(getName()+"释放锁资源 lock1");
lock1.unlock();
}
}
}