文章目录
多线程
程序:死的静态的功能的集合
进程:当程序运行起来就会开辟一个进程,多次运行程序会开辟多个进程
线程:一个进程内可以存在多条线程至少有一个线程
并行:多核CPU处理多个任务,多个任务同时进行
并发:单核cpu为例:貌似是一起执行,实际是在不停切换
如何编写多线程程序
继承Thread类
采用继承的方式创建多线程
1.创建一个类继承Thread类
2.重写run()方法
3.启动线程:创建线程对象,使用线程对象调用start()方法来完成启动
Thread.currentThread().setName();设置当前线程名字
Thread.currentThread().getName();获取当前线程名字
public class Test {
public static void main(String[] args) {
//创建兔子线程
RabbitThread rabbitThread = new RabbitThread();
rabbitThread.setName("小兔子");//设置兔子线程名字
rabbitThread.start();
Thread.currentThread().setName("小乌龟");//设置当前线程名字
while (true){
System.out.println(Thread.currentThread().getName()+"跑");
}
}
}
class RabbitThread extends Thread{
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName()+"跑");
}
}
}
实现Runnable接口
实现的方式创建多线程
1.创建一个类实现Runnable接口
2.重写run()方法
3.创建Runnable对象
4.创建Thread类,将Runnable对象作为参数进行传递
5.调用start()方法启动线程
继承的方式与实现的方式做对比
1.继承的方式简单
2.实现的方式可以更好地实现资源共享
public class Test {
public static void main(String[] args) {
//创建Runnable对象
RabbitRunnable rabbitThread = new RabbitRunnable();
//创建Thread类,将Runnable对象作为参数进行传递
Thread thread = new Thread(rabbitThread);
thread.setName("小兔子");//设置兔子线程名字
thread.start();//开启线程
Thread.currentThread().setName("小乌龟");//设置当前线程名字
while (true){
System.out.println(Thread.currentThread().getName()+"跑");
}
}
}
class RabbitRunnable implements Runnable{
@Override
public void run() {
while (true){
System.out.println(Thread.currentThread().getName()+"跑");
}
}
}
可以采用匿名内部类的方式实现多线程
//创建了匿名子类对象
Thread t1 = new Thread( ) {
@Override
public void run() {
while (true) {
System.out.println("乌龟跑");
}
}
};
t1.start();
new Thread(new Runnable() {//创建了一个Runnable的匿名子类对象
@Override
public void run() {
while (true) {
System.out.println("兔子跑");
}
}
}).start();
采用构造器的方式实现多线程
public class TestThreadConstructor {
public static void main(String[] args) {
/*RabbitThread r1 = new RabbitThread("灰兔子");
RabbitThread r2 = new RabbitThread("白兔子");
r1.start();
r2.start();*/
TortoiseRunnable tortoiseRunnable = new TortoiseRunnable();
Thread thread = new Thread(tortoiseRunnable);
thread.setName("小乌龟");
thread.start();
Thread thread1 = new Thread(tortoiseRunnable,"忍者神龟");
thread1.start();
}
}
class RabbitThread extends Thread{
public RabbitThread(String name) {
super(name);
}
@Override
public void run() {
while (true){
System.out.println(this.getName()+"跑");
}
}
}
class TortoiseRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"跑");
}
}
}
常用方法
1.isAlive():判断当前线程是否处在活跃状态
2.setPriority(优先级):1<=优先级<=10
如果没有指定优先级,默认都是5
优先级特别低也会有执行的机会
先去执行优先级高的再去执行优先级低的
3.Thread.sleep(数字);:让当前线程睡眠一段时间(让当前线程进入阻塞状态)
单位是ms
4.join():强行插队,插队的线程调用join()方法,被插队的线程要进行等待,等插队的线程执行完毕后才能继续执行
join(2000):强行插队,插队的线程调用join()方法,被插队的线程要进行等待2000ms,等插队的线程执行完毕后或者时间已到,才能继续执行
5.yield();:线程的礼让(特别不明显)
将线程从运行状态转为就绪状态
6.stop():结束当前线程
7.setDaemon(true):守护线程
哪一个线程调用了该方法就是守护线程
8.volatile:保证线程间的数据的可见性
实现Callable接口
线程池
线程安全
- 线程安全问题:当多个线程操作共享数据时,有可能会发生线程安全问题
- 解决线程安全问题
- 注意: 局部变量不能作为共享资源
同步代码块
语法结构
synchronized(同步监视对象){
]
同步监视对象:
1.必须是引用数据类型
2.当多条线程操作共享数据,同步监视对象,必须是同一个
3.同步代码块意义:保证在同步代码块内只要一条线程在执行,其他线程需要在同步代码块外等待
4.当一条线程进入同步代码块内,那么其他线程不能进入拥有同一个同步监视器对象的同步代码块
同步方法
同步方法的同步监视器是this
当有一条线程进入同步方法,那么其他线程不仅不能进入此方法,也不能进入拥有同一个人同步监视器对象的同步方法
public class Test {
public static void main(String[] args) {
WindowRunnable windowRunnable = new WindowRunnable();
Thread t1 = new Thread(windowRunnable,"窗口一");
Thread t2 = new Thread(windowRunnable,"窗口二");
Thread t3 = new Thread(windowRunnable,"窗口三");
t1.start();
t2.start();
t3.start();
}
}
class WindowRunnable implements Runnable{
int count = 60;
@Override
public void run() {
while (true){
if (count <= 0){
break;
}
saleTicket();
}
}
private synchronized void saleTicket() {
if (count<=0){
return;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"卖了第"+count+"张票");
count--;
}
}
Lock:公平锁和非公平锁
单例模式
一种代码模板
单例模式:包装产生的对象是唯一的,无论产生多少个对象,都是唯一的
单例模式分为两种:
1. 饿汉式:一开始创建类的时候,就把此对象创建出来
2. 懒汉式:等到需要的时候,再次创建出来
懒汉式可能出现线程安全问题
public class Test {
public static void main(String[] args) {
HungerSingle i1 = HungerSingle.INSTANCE;
HungerSingle i2 = HungerSingle.INSTANCE;
System.out.println(i2==i1);//true
HungerSingle1 i3 = HungerSingle1.INSTANCE;
HungerSingle1 i4 = HungerSingle1.INSTANCE;
System.out.println(i3==i4);//true
LazySingle i5 = LazySingle.getInstance();
LazySingle i6 = LazySingle.getInstance();
System.out.println(i5 == i6);//true
}
}
class HungerSingle{
public static final HungerSingle INSTANCE = new HungerSingle();
private HungerSingle() {
}
}
enum HungerSingle1{
INSTANCE
}
class LazySingle{
private static LazySingle lazySingle;
private LazySingle(){
}
public static synchronized LazySingle getInstance(){
if (lazySingle == null) {
synchronized (Test.class){
if (lazySingle == null) {
lazySingle = new LazySingle();
}
}
}
return lazySingle;
}
}
等待唤醒机制
线程通信:
- wait():进行等待
- wait(时间):
- notify:通知;如果有多个线程等待,则随机通知一个线程
- notifyAll():通知所有等待的线程干活
线程通信使用同步代码块实现
/*
一个吧台 一个厨师 一个服务员
吧台最多放10份菜
厨师已经做了10道菜则进行等待,通知服务员上菜
服务员上菜,当服务员把所有的菜都上完了,服务员等待,通知厨师做菜
问题1:厨师和服务员使用两个吧台
解决:将吧台声明到Test类中
问题2:厨师还没做菜,服务员已经开始上菜
解决:保证线程安全,使用同步代码块
问题3.厨师做的菜已经超出了吧台的能力,服务员端的菜是负数(无)
解决:当吧台的菜已经<0时,服务员等待;当吧台的菜>0时,厨师等待
wait()释放锁
*/
public class Test {
public static void main(String[] args) {
Bar bar = new Bar();
CookThread cookThread = new CookThread(bar);
cookThread.setName("厨师");
WaiterThread waiterThread = new WaiterThread();
waiterThread.setBar(bar);
waiterThread.setName("服务员");
cookThread.start();
waiterThread.start();
}
}
class Bar{
public static final int MAX_NUM = 10;//只能放10道菜
int count;//记录吧台上菜的数量
}
class CookThread extends Thread{
private Bar bar;
public CookThread(Bar bar) {
this.bar = bar;
}
@Override
public void run() {
while (true){
//同步代码块 :保证线程安全
synchronized (bar){//同步监视器使用共享资源
//厨师在做菜之前对数据进行校验
if (bar.count >= Bar.MAX_NUM) {//厨师做的菜已经>=吧台能够放菜的最大份数
try {
bar.wait();//厨师等待:采用同步监视器调用
} catch (InterruptedException e) {
e.printStackTrace();
}
}
bar.count++;//厨师做了一道菜
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName()+"做了一道菜,现在有"+bar.count+"份菜");
bar.notify();//通知服务员端菜
}
}
}
}
class WaiterThread extends Thread{
private Bar bar;
public void setBar(Bar bar) {
this.bar = bar;
}
@Override
public void run() {
while (true) {
synchronized (bar) {
//在端菜之前进行校验
if (bar.count <= 0) {
try {
bar.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
bar.count--;//服务员端走了一份菜
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName() + "端走了一道菜,还有" + bar.count + "份菜");
bar.notify();//通知厨师做菜
}
}
}
}
了解死锁
/*
产生死锁的原因:两条线程互相持有对方的锁资源,不放松
可以让一条线程先跑完再让另一条线程在执行
* */
public class TestDeadLock {
public static void main(String[] args) {
Object goods = new Object();
Object money = new Object();
GoodsThread goodsThread = new GoodsThread(goods, money);
CustomerThread customerThread = new CustomerThread(goods, money);
goodsThread.start();
try {
Thread.sleep(1000);// 可以让一条线程先跑完再让另一条线程在执行
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
customerThread.start();
}
}
class GoodsThread extends Thread{
Object goods;
Object money;
public GoodsThread(Object goods, Object money) {
this.goods = goods;
this.money = money;
}
@Override
public void run() {
synchronized (money) {
System.out.println("先给钱");
synchronized (goods){
System.out.println("再发货");
}
}
}
}
class CustomerThread extends Thread{
Object goods;
Object money;
public CustomerThread(Object goods, Object money) {
this.goods = goods;
this.money = money;
}
@Override
public void run() {
synchronized (goods) {
System.out.println("先发货");
synchronized (money){
System.out.println("再给钱");
}
}
}
}
线程通信使用同步方法实现
/*
同步方法也有同步监视器对象:this,想要实现线程安全,this必须是相同的的
线程通信方法,必须位于同步方法或者同步代码块内
*/
public class Test1 {
public static void main(String[] args) {
Bar1 bar1 = new Bar1();
CookRunnable cookRunnable = new CookRunnable(bar1);
Thread t0 = new Thread(cookRunnable, "厨师");
WaiterRunnable waiterRunnable = new WaiterRunnable(bar1);
Thread t1 = new Thread(waiterRunnable, "服务员1");
Thread t2 = new Thread(waiterRunnable, "服务员2");
t0.start();
t1.start();
t2.start();
}
}
class CookRunnable implements Runnable{
private Bar1 bar1;
public CookRunnable(Bar1 bar1) {
this.bar1 = bar1;
}
@Override
public void run() {
while (true){
bar1.Put();
}
}
}
class WaiterRunnable implements Runnable{
private Bar1 bar1;
public WaiterRunnable(Bar1 bar1) {
this.bar1 = bar1;
}
@Override
public void run() {
while (true){
bar1.get();
}
}
}
class Bar1{
public static final int MAX_NUM = 10;//吧台最多放10份菜
int num;//统计菜数
//厨师做菜
public synchronized void Put(){
if (num >= MAX_NUM) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num++;
System.out.println(Thread.currentThread().getName()+"做了一道菜,现在有"+num+"道菜");
//this.notify();
this.notifyAll();
}
//服务员端菜
public synchronized void get(){
while (num<=0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println(Thread.currentThread().getName()+"端走了一道菜,还有"+num+"道菜");
this.notify();
}
}
线程生命周期
- 观点1
- 观点2