文章目录
<font color=#999AAA
提示:以下是本篇文章正文内容,下面案例可供参考
一、多线程是什么?
其实多线程在生活中,非常常见,比如我们平时去上厕所的时候总不能少了一样非常重要的东西,手机!(卫生纸也重要),那么我们一边玩手机一边上厕所就可以简单的理解为一个多线程。现实中有很多这种例子,看起来都在同时做,但本质上我们大脑同一时间只做了同一件事,只是切换的比较快。
二、进程和线程
首先,要理解进程与线程的定义分别是什么。进程是并发执行的程序在执行过程中分配和管理资源的基本单位。线程是进程的一个执行单元,是比进程还要小的独立运行的基本单位。一个程序至少有一个进程,一个进程至少有一个线程。
根本区别:进程是资源分配最小单位,线程是程序执行的最小单位。 计算机在执行程序时,会为程序创建相应的进程,进行资源分配时,是以进程为单位进行相应的分配。每个进程都有相应的线程,在执行程序时,实际上是执行相应的一系列线程。
三、线程创建的三种方式
1. 继承Thread类
package wx.Thread;
/**
* 创建线程的方法
* 1.继承Thread类
* 重写run方法
* 调用start启动线程
*
* 结果分析
* 执行结果中,两个线程交替执行
* 结果交替输出,需要数据足够多的时候才会出现这种情况
* 线程不一定立即执行,由cpu暗排调度
*/
public class ThreadDemo1 extends Thread{
// run方法
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println( "我在看代码"+i);
}
}
// 主线程
public static void main(String[] args) {
// 创建线程对象
ThreadDemo1 threadDemo1=new ThreadDemo1();
threadDemo1.start();
for (int i = 0; i < 2000; i++) {
System.out.println("我在主线程"+i);
}
}
}
2.实现Runnable接口
好处:避免单继承的局限,一个线程可以被多个对象使用
package wx.Thread;
/**
*1.实现Runnable创建线程
*2.重写run方法
* 3.创建线程对象,执行线程需要丢如runnable实现类,启动start方法
*/
public class ThreadDemo2 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println( "我在看代码"+i);
}
}
// 主线程
public static void main(String[] args) {
// 创建runnable实现类
ThreadDemo2 threadDemo2=new ThreadDemo2();
// 创建线程对象 代理
Thread thread=new Thread(threadDemo2);
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在主线程"+i);
}
}
}
一般情况下,都推荐使用这种方式
三 . 实现Callable接口
略
三、线程的五大状态
(1)新建状态:即单纯地创建一个线程,创建线程有三种方式。
(2)就绪状态:在创建了线程之后,调用Thread类的start()方法来启动一个线程,即表示线程进入就绪状态!
(3)运行状态:当线程获得CPU时间,线程才从就绪状态进入到运行状态!
(4)阻塞状态:线程进入运行状态后,可能由于多种原因让线程进入阻塞状态,如:调用sleep()方法让线程睡眠,调用wait()方法让线程等待,调用join()方法、suspend()方法(它现已被弃用!)以及阻塞式IO方法。
(5)死亡状态:run()方法的正常退出就让线程进入到死亡状态,还有当一个异常未被捕获而终止了run()方法的执行也将进入到死亡状态!
四、线程休眠sleep和wait的异同
相同点:一旦执行方法以后,都会使得当前的进程进入阻塞状态。
不同点:
1.两个方法声明的位置不同,Thread类中声明sleep,Object类中声明wait。
2.调用的要求不同,sleep可以在任何需要的场景下调用,wait必须使用在同步代码块或者同步方法中。
3.关于是否释放同步监视器,如果两个方法都使用在同步代码块或同步方法中,sleep不会释放,wait会释放。
五、join强制加入方法
join方法会强行让正在执行的线程进入阻塞状态,优先执行自己,就像人民币玩家。
package wx.Thread;
public class JoinDemo1 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("线程VIP来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
// 启动线程
JoinDemo1 joinDemo1=new JoinDemo1();
Thread thread=new Thread(joinDemo1);
thread.start();
// 主线程
for (int i = 0; i < 500; i++) {
if (i == 200) {
thread.join();
}
System.out.println("main"+i);
}
}
}
六、锁
为什么要加锁?
多线程保证线程安全就是让多个线程执行的情况和单线程一样,读的时候是不影响线程安全的,但如果对数据操作,增加或者删除,几个线程同时进行,就会发生删多或者增加多的情况,这时候为了保证线程安全,就可以加对象锁
Synchronized锁
方式一:同步代码块
使用同步监视器(锁)
Synchronized(同步监视器){
//需要被同步的代码
}
方式二:同步方法
将同步代码块提取出来成为一个方法,用synchronized关键字修饰此方法。
}
代码演示(买火车票)
package wx.Thread;
/**
* 多个线程操作同一个对象
* 买火车票的例子
*
*
* 结果总结:线程不安全
*/
public class ThreadDemo3 implements Runnable {
//票数
private int ticketNums=10;
@Override
// synchronized线程同步
public synchronized void run() {
while (true){
if (ticketNums<=0){
break;
}
// 延迟模型
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了"+ticketNums+"票");
ticketNums--;
}
}
public static void main(String[] args) {
// 创建runnable实现类
ThreadDemo3 threadDemo3=new ThreadDemo3();
new Thread(threadDemo3,"小明").start();
new Thread(threadDemo3,"小黄").start();
new Thread(threadDemo3,"花鸟名").start();
}
}
代码演示(两个人同时取钱)
package wx.Thread.ThreadShiyan.demo3;
public class Account {
int money; //余额
String name ;//卡名
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
package wx.Thread.ThreadShiyan.demo3;
// 取钱线程
public class getMoney implements Runnable {
// 账户
Account account;
public void setAccount(Account account) {
this.account = account;
}
private int getMoney;
public void setGetMoney(int getMoney) {
this.getMoney = getMoney;
}
// 取钱 同步方法
@Override
public synchronized void run() {
if ((account.money-getMoney)>=0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money=account.money-getMoney;
System.out.println(Thread.currentThread().getName()+"取了"+getMoney+"元");
System.out.println("账户余额"+account.money);
}else{
System.out.println("账户余额不足");
}
}
// 取钱 同步代码块
// @Override
// public void run() {
// synchronized (account){
// if ((account.money-getMoney)>=0){
// account.money=account.money-getMoney;
// System.out.println(Thread.currentThread().getName()+"取了"+getMoney+"元");
// System.out.println("账户余额"+account.money);
// }else{
// System.out.println("账户余额不足");
// }
// }
//
// }
}
package wx.Thread.ThreadShiyan.demo3;
public class test {
public static void main(String[] args) {
// 创建一个账户
Account account=new Account(1000,"wx");
getMoney getMoney=new getMoney();
getMoney.setGetMoney(600);
getMoney.setAccount(account);
new Thread(getMoney,"妻子").start();
new Thread(getMoney,"张三").start();
}
}
JDK5.0新增的lock锁方法
代码演示
package wx.Thread;
import java.util.concurrent.locks.ReentrantLock;
public class TestLock {
public static void main(String[] args) {
TestLock1 testLock1=new TestLock1();
new Thread(testLock1,"小明").start();
new Thread(testLock1,"小黄").start();
new Thread(testLock1,"小兰").start();
}
}
class TestLock1 implements Runnable{
// 定义锁
private ReentrantLock lock=new ReentrantLock();
int tickets=10;
@Override
public void run() {
while (true){
try {
lock.lock();//加锁
if (tickets>=0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+tickets--);
}else{
break;
}
}finally {
lock.unlock();//解锁
}
}
}
}
Synchronized与lock的异同?
1.相同
二者都可以解决线程安全问题
2.不同
synchronized机制在执行完相应的代码逻辑以后,自动的释放同步监视器
lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())(同时以为着lock的方式更为灵活)
死锁解决办法
1.减少同步共享变量
2.采用专门的算法,多个线程之间规定先后执行的顺序,规避死锁问题。
3.减少锁的嵌套。
六、线程池
创建方式
1)newCachedThreadPool创建一个可缓存线程池
2)newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数。
3)newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
4)newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务。
简单代码演示
package wx.Thread.ThreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 测试线程池
*
*/
public class TestPool {
public static void main(String[] args) {
// 创建线程池
ExecutorService executorService= Executors.newFixedThreadPool(10);
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.execute(new MyThread());
// 关闭
executorService.shutdown();
}
}
//线程
class MyThread implements Runnable{
@Override
public void run() {
System.out.println( Thread.currentThread().getName());
}
}
package wx.Thread.ThreadPool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 测试线程池
*
*/
public class TestPool {
public static void main(String[] args) {
// 创建线程池
ExecutorService executorService= Executors.newFixedThreadPool(10);
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.execute(new MyThread());
executorService.execute(new MyThread());
// 关闭
executorService.shutdown();
}
}
//线程
class MyThread implements Runnable{
@Override
public void run() {
System.out.println( Thread.currentThread().getName());
}
}
执行过程