给出最后 i、j、k 的值
public static void main(String[] args) {
int i = 1;
i = i++;
int j = i++;
int k = i + ++i * i++;
System.out.println("i=" + i);
System.out.println("j=" + j);
System.out.println("k=" + k);
}
结果: i = 4, j = 1, k = 11;
过程如下:
小结:
- 赋值操作=最后计算,将操作数栈赋值到局部变量表;
- =右侧元素从左到右加载值依次压入操作数栈;
- 操作数栈中的运算要依照运算符优先级;
- 自增、自减操作直接在局部变量表中改变变量的值,不经过操作数栈;
- 最后的赋值之前,临时结果也存储在操作数栈中。
单例模式是Java中最简单的设计模式之一,这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
注:
- 单例类只能有一个实例:构造器私有化;
- 单例类必须自己创建自己的唯一实例:含有一个该类的静态变量来保存这个唯一的实例。
- 单例类必须给所有其他对象提供这一实例:对外提供获取该实例对象:(1)直接暴露;(2)用静态变量的get方法获取;
几种常见形式:
饿汉式 ,直接创建对象,不存在线程安全问题,具体分为:
- 直接实例化饿汉式(简洁直观)
- 枚举式(最简洁)
- 静态代码块饿汉式(适合复杂实例化)
懒汉式, 延迟创建对象,具体分为:
- 线程不安全(适用于单线程)
- 线程安全(适用于多线程)
- 静态内部类形式(适用于多线程)
保证在没有锁的状态下,多线程下保持一一致性实现值的改变。
ABA问题:分手,找别人,复合;中间的经历无法感知。
AtomicInteger
JUC是java.util.concurrent工具包的简称,这是一个处理线程的工具包,从JDK1.5开始出现。
进程与线程进程Process:是系统进行资源分配和调度的基本单位,是操作系统结构的技术。进程是线程的容器,程序是指令、数据及组织形式的描述,进程是程序的实体。
线程Thread:是操作系统能够进行运算掉的最小单位,它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
线程状态枚举类
public enum State {
NEW,(新建)
RUNNABLE,(准备就绪),
BLOCKED,(阻塞)
WAITING,(等待:不见不散,约个人五点到,五点不到会继续等)
TIMED_WAITING,(等待:过时不候,约定时间未到直接走人)
TERMINATED;(终结)
}
wait,sleep区别
- sleep()是Thread的静态方法;wait()是Object的方法,任何对象实例都能调用。
- sleep()执行时不会释放锁,也不需要占用锁;wait会释放锁,但是调用的前提是当前线程占有锁(即代码要在synchronized中)。
- 都可以被interrupted()方法中断,都是在哪里睡哪里醒。
管程即为Monitor监视器,保证了同一时刻只有一个进程在管程内活动,即管程内定义的操作在同一时刻只能被一个线程访问被保护的数据或代码。
JVM同步基于进入加锁和退出解锁,使用管程对象实现的,每个Java对象都一个管程对象,随着java对象一同创建和销毁。
用户线程:平时用到的普通线程,如自定义线程。当主线程结束后,用户线程还在运行,JVM存活。
守护线程:运行在后台的特殊线程,比如垃圾回收。如果没有用户线程了,只剩下守护线程,jvm结束。守护线程的设置setDaemon(true)要在线程运行之前。
synchronized是Java中的关键字,是一种同步锁,它修饰的对象有以下几种:
- 修饰一个代码块,作用的对象是对用这个代码块的对象;
synchronized(this){
//修饰的是大括号中的代码
}
- 修饰一个方法,被修饰的方法称为同步方法,其作用范围是整个方法,作用对象是调用这个方法的对象。
- 修饰一个静态方法,作用范围是整个静态方法,作用的对象是这个类的所有对象。
- 修饰一个类;
多线程编程步骤:
- 创建资源类,在资源类创建属性和操作方法(高内聚,低耦合:资源有什么就调用什么,没有就不调用)。
- 创建多个线程,调用资源类的操作方法。
通过synchronized实现一个卖票实例,3个售票员卖出30张票,票类Ticket有属性number记录票数目,方法sale()进行卖票并且通过synchronized加锁,
package com.jiayuleng.jucstudy.sync;
class Ticket {
// 票数
private int number = 30;
// 操作方法:买票
public synchronized void sale() {
// 判断:是否有票;
if(number > 0) {
System.out.println(Thread.currentThread().getName() + ": 卖出:" + (number--) + " 剩下:" + number);
}
}
}
public class SaleTicket {
// 第二步 创建多个线程,调用资源类的操作方法
public static void main(String[] args) {
Ticket ticket = new Ticket();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 40; i++) {
ticket.sale();
}
}
}, "AA").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 40; i++) {
ticket.sale();
}
}
}, "bb").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 40; i++) {
ticket.sale();
}
}
}, "cc").start();
}
}
如果不加锁,会出现线程不同步的情况。
- 创建一个继承于Thread类的子类。
- 重写Thread类的run()方法,将此线程执行的操作声明在run()中。
- 创建Thread类的子类的对象。
- 通过此类调用start()方法。
例,通过子线程输出[0, 100)的偶数
package com.jiayuleng.javabasestudy.threadstudy.threadcreate;
// 1.创建一个继承于Thread类的子类。
// 2.重写Thread类的run()方法,将此线程执行的操作声明在run()中。
// 3.创建Thread类的子类的对象。
// 4.通过此类调用start()方法。
//例子:遍历100以内的所有的偶数
class MyThread extends Thread {
@Override
public void run() {
for(int i = 0; i < 100; i++) {
if(i % 2 == 0) {
System.out.println(i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
for(int i = 0; i < 100; i++) {
if(i % 2 == 0) {
System.out.println(i + "******main*******");
}
}
}
}
主线程与子线程交替运行如下图
问题一:我们不能通过直接调用run()的方式启动线程,start()方法会先启动当前线程,再调用当前线程的run()方法。
问题二:同一个线程对象不可以启动start()方法两次,会报IllegalThreadStateException异常;



