一、多线程概述
1、进程:正在进行中的程序
2、线程:就是进程中一个负责程序执行的控制单元(执行路径)
一个进程中,可以有多个执行路径,即多线程
一个进程中,至少有一个执行路径。
(多线程其实就是多个线程中的快速切换)
二、多线程的创建方式①--继承Thread类
继承Thread类,重写run方法
1、JVM创建的主线程的任务都定义在了主函数中
2、Thread类中的run方法就是封装自定义线程任务的函数,即run方法相当于一个main方法
package com.lee.juc;
public class ThreadDemo_01 {
public static void main(String[] args) {
Demo demo1 = new Demo("张三");
Demo demo2 = new Demo("LeeSi");
demo1.start();
demo2.start();
System.out.println("=======结束========");
}
}
class Demo extends Thread{
private String name;
@Override
public void run() {
for(int i=0;i<10;i++) {
System.out.println(name+"====>"+i);
}
}
public Demo() {
super();
}
public Demo(String name) {
this.name = name;
}
}
结果:
================================
注:调用Run和Start有什么区别?
================================
方法:
getName 获取线程名称
CurrentThread 返回当前正在执行线程的引用对象
三、多线程的创建方式②--实现Runnable接口
package com.lee.juc;
public class ThreadDemo_02 {
public static void main(String[] args) {
Demo2 d = new Demo2();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
}
}
class Demo2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i+"====>"+Thread.currentThread().getName());
}
}
}
结果:
===========================
注:通过implement Runnable接口的方法
避免了单继承 extends Thread的局限性
===========================
四、线程状态
再增加一个临时阻塞状态
五、卖票小程序
需求:四个窗口共同卖100张票
1、继承Thread方法
NUM为static
package com.lee.juc;
public class TicketDemo_01 {
public static void main(String[] args) {
Ticket_01 t1 = new Ticket_01();
Ticket_01 t2 = new Ticket_01();
Ticket_01 t3 = new Ticket_01();
Ticket_01 t4 = new Ticket_01();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket_01 extends Thread{
private static int NUM = 100;
@Override
public void run() {
while(true) {
if(NUM>0) {
System.out.println(Thread.currentThread().getName()+"...sale.."+NUM--);
}else {
break;
}
}
}
}
结果:
2、实现Runnable接口
package com.lee.juc;
public class TicketDemo_02 {
public static void main(String[] args) {
Ticket_02 ticket = new Ticket_02();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
Thread t4 = new Thread(ticket);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket_02 implements Runnable{
private int NUM = 100;
@Override
public void run() {
while(true) {
if(NUM>0) {
System.out.println(Thread.currentThread().getName()+"...sale.."+NUM--);
}else {
break;
}
}
}
}
结果:
六、线程安全问题现象
package com.lee.juc;
public class TicketDemo_03 {
public static void main(String[] args) {
Ticket_03 ticket = new Ticket_03();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
Thread t4 = new Thread(ticket);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket_03 implements Runnable{
private int NUM = 100;
@Override
public void run() {
while(true) {
if(NUM>0) {
//======加sleep=======
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"...sale.."+NUM--);
}else {
break;
}
}
}
}
结果:
产生原因:
①、多个线程,操作同一个共享数据
②、操作共享数据的线程代码有多条
七、同步代码块
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,其他线程不可以参与运算。
必须要当前线程把这些代码都执行完后,其他线程才能执行。
同步代码块的格式:
synchronized(对象){
需要被同步的代码;
}
代码:
package com.lee.juc;
public class TicketDemo_03 {
public static void main(String[] args) {
Ticket_03 ticket = new Ticket_03();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
Thread t3 = new Thread(ticket);
Thread t4 = new Thread(ticket);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Ticket_03 implements Runnable{
private int NUM = 100;
Object obj = new Object();
@Override
public void run() {
while(true) {
//====================
synchronized (obj) {
if(NUM>0) {
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"...sale.."+NUM--);
}else {
break;
}
}
//=====================
}
}
}
结果:
同步的好处和弊端:
好处:解决了线程安全的问题
弊端:相对降低了效率
注:
必须保证多个线程使用同一个锁
八、同步函数
需求:
两个储户去银行存钱,每个人存3次,每次存¥100
1、未加锁的错误代码
package com.lee.juc;
public class BankDemo_01 {
public static void main(String[] args) {
Customer cus = new Customer();
Thread t1 = new Thread(cus);
Thread t2 = new Thread(cus);
t1.start();
t2.start();
}
}
class Bank{
private int SUM;
public void add(int num) {
SUM = SUM +num;
System.out.println("current bank count money : ==>"+SUM);
}
}
class Customer implements Runnable{
//两人公用一个bank,所以bank提出来
private Bank bank = new Bank();
@Override
public void run() {
for(int i=0;i<3;i++) {
bank.add(100);
}
}
}
2、加锁,正确代码
package com.lee.juc;
import org.omg.Messaging.SyncScopeHelper;
public class BankDemo_01 {
public static void main(String[] args) {
Customer cus = new Customer();
Thread t1 = new Thread(cus);
Thread t2 = new Thread(cus);
t1.start();
t2.start();
}
}
class Bank{
private int SUM;
public void add(int num) {
SUM = SUM +num;
System.out.println("current bank count money : ==>"+SUM);
}
}
class Customer implements Runnable{
//两人公用一个bank,所以bank提出来
private Bank bank = new Bank();
@Override
public void run() {
for(int i=0;i<3;i++) {
//===========加锁
synchronized(bank) {
bank.add(100);
}
//============
}
}
}
或者
package com.lee.juc;
public class BankDemo_01 {
public static void main(String[] args) {
Customer cus = new Customer();
Thread t1 = new Thread(cus);
Thread t2 = new Thread(cus);
t1.start();
t2.start();
}
}
class Bank{
private int SUM;
//同步函数锁
public synchronized void add(int num) {
SUM = SUM +num;
System.out.println("current bank count money : ==>"+SUM);
}
}
class Customer implements Runnable{
//两人公用一个bank,所以bank提出来
private Bank bank = new Bank();
@Override
public void run() {
for(int i=0;i<3;i++) {
bank.add(100);
}
}
}
结果:
九、验证同步函数的锁
1、验证是否是Object
package com.lee.juc;
public class TicketDemo_04 {
public static void main(String[] args) {
Ticket_04 ticket = new Ticket_04();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
t1.start();
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
ticket.flag = false;
t2.start();
}
}
class Ticket_04 implements Runnable{
private int NUM = 100;
boolean flag = true;
Object obj = new Object();
@Override
public void run() {
if(flag) {
while(true) {
synchronized (obj) {
if(NUM>0) {
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"......同步代码块....."+NUM--);
}
}
}
}else {
while(true) {
this.sale();
}
}
}
public synchronized void sale() {
if(NUM>0) {
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"...同步函数.."+NUM--);
}
}
}
结果:
错误,证明不是obj
2、证明是不是this
package com.lee.juc;
public class TicketDemo_04 {
public static void main(String[] args) {
Ticket_04 ticket = new Ticket_04();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
t1.start();
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
ticket.flag = false;
t2.start();
}
}
class Ticket_04 implements Runnable{
private int NUM = 100;
boolean flag = true;
Object obj = new Object();
@Override
public void run() {
if(flag) {
while(true) {
synchronized (this) {
if(NUM>0) {
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"......同步代码块....."+NUM--);
}
}
}
}else {
while(true) {
this.sale();
}
}
}
public synchronized void sale() {
if(NUM>0) {
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"...同步函数.."+NUM--);
}
}
}
结果:
证明是this
十、验证静态同步函数的锁
代码:
package com.lee.juc;
public class TicketDemo_05 {
public static void main(String[] args) {
Ticket_05 ticket = new Ticket_05();
Thread t1 = new Thread(ticket);
Thread t2 = new Thread(ticket);
t1.start();
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
ticket.flag = false;
t2.start();
}
}
class Ticket_05 implements Runnable{
private static int NUM = 100;
boolean flag = true;
Object obj = new Object();
@Override
public void run() {
if(flag) {
while(true) {
synchronized (this.getClass()) {
if(NUM>0) {
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"......同步代码块....."+NUM--);
}
}
}
}else {
while(true) {
this.sale();
}
}
}
public static synchronized void sale() {
if(NUM>0) {
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"...同步函数.."+NUM--);
}
}
}
结果:
注:
静态同步函数的锁是 当前文件的字节码,this.getClass或TicketDemo_05.class
十一、单例模式
//懒汉式
class SingleLazy{
private static SingleLazy singleLazy;
private SingleLazy() {}
private static SingleLazy getInstance() {
if(singleLazy==null) {
synchronized (SingleLazy.class) {
if(singleLazy==null) {
singleLazy = new SingleLazy();
}
}
}
return singleLazy;
}
}
//饿汉式--推荐使用这个
class SingleHungry{
private static SingleHungry singleHungry = new SingleHungry();
private SingleHungry() {}
private static SingleHungry getInstance() {
return singleHungry;
}
}
十二、死锁的情景
产生情景:
1、同步的嵌套:A锁里边有B锁,B锁里边有A锁。
package com.lee.juc;
public class DeadLockTest {
public static void main(String[] args) {
DeadLock deadLock = new DeadLock();//flag = true
Thread t1 = new Thread(deadLock);
Thread t2 = new Thread(deadLock);
t1.start();
try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
deadLock.flag = false;
t2.start();
}
}
class DeadLock implements Runnable{
public boolean flag = true;
private Object obj = new Object();
public DeadLock() {}
public DeadLock(boolean flag) {
this.flag = flag;
}
public void run() {
if(flag) {
while(true) {
synchronized (this) {
System.out.println("...if...this...lock.."+Thread.currentThread().getName());
synchronized (obj) {
System.out.println("...if...obj...lock.."+Thread.currentThread().getName());
}
}
}
}else {
while(true) {
synchronized (obj) {
System.out.println("...else...obj...lock.."+Thread.currentThread().getName());
synchronized (this) {
System.out.println("...else...this...lock.."+Thread.currentThread().getName());
}
}
}
}
}
}
结果: