一、线程
@SuppressWarnings({"all"}) //取消文件警告
public class Hello {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
//获取当前cpu的数量
int i = runtime.availableProcessors();
System.out.println("当前cpu的数量" + i);
}
}
二、继承Threa创建线程
@SuppressWarnings({"all"}) //取消文件警告
public class Hello {
public static void main(String[] args) {
//创建Cat对象,可以当作线程使用
Cat cat = new Cat();
cat.start();//启动线程
}
}
//1.当一个类继承Thread类,该类就可以当作线程使用
//2.我们会重写run方法,写上自己的业务代码
//3.run Thread 类 实现了Runnable 接口的 run方法
class Cat extends Thread {
@Override
public void run() { //重写run方法,写上自己的业务逻辑
while (true) {
//该线程每隔1秒,在控制台输出“喵喵,我是小猫咪”
System.out.println("喵喵,我是小猫咪" + "当前线程名"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
三、通过实现Runnable接口创建线程
@SuppressWarnings({"all"}) //取消文件警告
public class Hello {
public static void main(String[] args) {
Cat cat = new Cat();
Thread thread = new Thread(cat);
thread.start();
}
}
class Cat implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("喵喵,我是小猫咪" + "当前线程名"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
案例:实现多个线程;命令行输入:jconsole查看线程 (建议使用Runnable)
@SuppressWarnings({"all"}) //取消文件警告
public class Hello {
public static void main(String[] args) {
Thread thread1 = new Thread(new Cat());
Thread thread2 = new Thread(new Dog());
thread1.start();
thread2.start();
}
}
class Cat implements Runnable{
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
System.out.println("猫");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Dog implements Runnable{
@Override
public void run() {
while (true){
try {
Thread.sleep(1000);
System.out.println("狗");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
四、线程常用方法
- setName 设置线程的名字
- getName 返回该线程的名字
- start 是该线程运行
- run 调用线程的run方法,这是普通调用
- setPriority 设置线程优先级
- getPriority 获取线程的优先级
- sleep 休眠
- interrupt 中断线程 唤醒睡眠
- yield 线程礼让,让出cpu但是不一定成功
- join 线程插队,一旦插队成功,这一定会执行完在释放
案例:创建一个子线程,每隔1秒输出hello,输出20次,主线程每隔1秒,输出hi,输出20次,要求两个线程同时执行,当主线程输出5次后,就让子线程再继续。
@SuppressWarnings({"all"}) //取消文件警告
public class Hello {
public static void main(String[] args) {
Say say = new Say();
Thread thread = new Thread(say);
thread.start();
for (int i = 0; i < 20; i++) {
if (i == 5){
try {
// Thread.yield(); //主线程让步
thread.join(); //子线程插队
} catch (Exception e) {
e.printStackTrace();
}
}{
try {
Thread.sleep(1000);
System.out.println("hi");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
class Say implements Runnable{
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
System.out.println("hello");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
五、守护线程
1.用户线程:也叫工作线程,当线程的任务的执行完成或通知方法结束
2.守护线程:一般是为工作线程服务的,当所有的用户线程结束,守护线程自动结束
3.常见的守护线程:垃圾回收机制
@SuppressWarnings({"all"}) //取消文件警告
public class Hello {
public static void main(String[] args) throws InterruptedException {
MyDaemonThread myDaemonThread = new MyDaemonThread();
//如果我们希望当main线程结束后子线程自动结束
//只需将子线程设置为守护线程即可 注意顺序先设置在执行
myDaemonThread.setDaemon(true);
myDaemonThread.start();
for (int i = 0; i < 10; i++) {
System.out.println("hi");
Thread.sleep(1000);
}
}
}
class MyDaemonThread extends Thread{
@Override
public void run() {
for (;;) {
try {
Thread.sleep(1000);
System.out.println("hello");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
六、线程的生命周期
七、线程同步机制
1.在多线程编程,一些敏感数据不允许被多个线程同时访问,此时就使用同步访问技术,保证数据在任何时候,最多有一个线程访问,以保证数据的完整性。2.也可以理解:当一个线程操作内存的时候其他线程不能对该内存进行操作(卖票案例)
@SuppressWarnings({"all"}) //取消文件警告
public class Hello {
public static void main(String[] args) throws InterruptedException {
SellTickets sellTickets1 = new SellTickets();
SellTickets sellTickets2 = new SellTickets();
SellTickets sellTickets3 = new SellTickets();
Thread thread1 = new Thread(sellTickets1);
Thread thread2 = new Thread(sellTickets2);
Thread thread3 = new Thread(sellTickets3);
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTickets implements Runnable {
public static int tickets = 100;
public static boolean loop = true;
public synchronized void sell() {
System.out.println(Thread.currentThread().getName() + "窗口卖出了一张票剩余" + --tickets);
if (tickets <= 0) {
loop = false;
System.out.println("售空");
}
}
@Override
public void run() {
while (loop) {
sell();
}
}
}
八、互斥锁
- java语言中引入了对象互斥锁的概念,来保证共享数据操作的完整性
- 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任意时刻,只能有一个线程访问该对象。
- 关键字synchronized来与对象的互斥锁联系,当某个对象用synchronized修饰时,表明该对象在任意时刻只能由一个线程访问
- 同步的局限性:导致程序的执行效率要降低
- 同步方法(非静态的)的锁可以是this,也可以是其他对象(要求是同一个对象)
- 同步方法(静态的)的锁为当前·类本身
@SuppressWarnings({"all"}) //取消文件警告
public class Hello {
public static void main(String[] args) throws InterruptedException {
SellTickets sellTickets1 = new SellTickets();
SellTickets sellTickets2 = new SellTickets();
SellTickets sellTickets3 = new SellTickets();
Thread thread1 = new Thread(sellTickets1);
Thread thread2 = new Thread(sellTickets2);
Thread thread3 = new Thread(sellTickets3);
thread1.start();
thread2.start();
thread3.start();
}
}
class SellTickets implements Runnable {
public static int tickets = 100;
public static boolean loop = true;
Object object = new Object();
//同步方法(静态的)的锁为当前·类本身
// public synchronized static void m1(){} 锁是加载在SellTickets.class上
public synchronized static void m1(){
}
//静态方法加载在类上
public static void m2(){
synchronized (SellTickets.class){
}
}
//说明
// public synchronized void sell() {} //就是一个同步方法,这时锁在 this对象
//也可以在代码块上写synchronized,同步代码块,互斥锁还是加载this对象
//同步代码块的对象变为Object,只要是同一个对象就可以
public /*synchronized*/ void sell() {
synchronized (/*this*/ object) {
System.out.println(Thread.currentThread().getName() + "窗口卖出了一张票剩余" + --tickets);
if (tickets <= 0) {
loop = false;
System.out.println("售空");
}
}
}
@Override
public void run() {
while (loop) {
sell();
}
}
}
注意事项和细节:同步方法如果没有使用staic修饰:默认锁对象为this。如果方法使用static修饰,默认对象为当前类.class
实现的落地步骤:需要先分析上锁的代码,选择同步代码块或同步方法,要求多个线程对象为同一个即可
九、线程的死锁
多个线程都占用了对方的锁资源,但不肯相让,导致死锁,在编程是一定要避免死锁的发生
@SuppressWarnings({"all"}) //取消文件警告
public class Hello {
public static void main(String[] args) throws InterruptedException {
new DeadLockDemo(true).start();
new DeadLockDemo(false).start();
}
}
class DeadLockDemo extends Thread{
static Object object1 = new Object();
static Object object2 = new Object();
boolean flag;
public DeadLockDemo(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag){
synchronized (object1){
System.out.println(Thread.currentThread().getName()+"进入1");
synchronized (object2){
System.out.println(Thread.currentThread().getName()+"进入2");
}
}
}else{
synchronized (object2){
System.out.println(Thread.currentThread().getName()+"进入3");
synchronized (object1){
System.out.println(Thread.currentThread().getName()+"进入4");
}
}
}
}
}
十、释放锁
(1)下面操作会释放锁:
- 当前线程的同步方法,同步代码块执行结束
- 当前线程在同代码块、同步方法遇到break,return。
- 当前线程在同步代码块,同步方法中出现了未处理的Error或Exception,导致异常结束
- 当前线程在同步代码块,同步方法中执行力线程对象的wait()方法,当前线程暂停,并释放锁
(2)下面操作不会释放锁:
- 线程执行同步代码块或同步方法时,程序调用Thread.sleep(),Thread.yield()方法暂停当前线程的执行,不会释放锁。
- 线程执行同步代码块,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁。suspend()和resume()方法已过时
十一、课后作业
(1)在main方法中启动两个线程,第1个线程循环随机打印100以内的整数,直到第2个线程从键盘读取了“Q”命令。
@SuppressWarnings({"all"}) //取消文件警告
public class Hello {
public static void main(String[] args) throws InterruptedException {
new PrintNum().start();
new ScannerQ().start();
}
}
class PrintNum extends Thread {
static boolean flg = true;
@Override
public void run() {
while (flg) {
try {
System.out.println("你好");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class ScannerQ extends Thread {
Scanner sc = new Scanner(System.in);
@Override
public void run() {
while (true) {
try {
Thread.sleep(5000);
System.out.println("请输入Q退出");
if (sc.next().toUpperCase().charAt(0) == 'Q') {
PrintNum.flg = false;
break;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
(2)存款10000,两个用户取款同一个卡,一次1000
@SuppressWarnings({"all"}) //取消文件警告
public class Hello {
public static void main(String[] args) throws InterruptedException {
T t = new T();
Thread thread1 = new Thread(t);
thread1.setName("爸爸");
Thread thread2 = new Thread(t);
thread2.setName("儿子");
thread1.start();
thread2.start();
}
}
//编程取款的线程
//1.因为这里涉及多个线程共享资源,所以我们使用实现Runnable方式
class T implements Runnable{
private int money = 10000;
@Override
public void run() {
while (true){
//1.这里使用synchronized 实现线程的同步
//2.当多个线程执行到这里时,就会去争取 this对象锁
//3.哪个线程夺取(this对象)就会执行synchronized代码块
//4.争取不到的,就blocked,准备夺取到
//5.this是一个非公平锁
synchronized (this){
//判断余额是否足够
if (money<=0){
System.out.println("余额不足");
break;
}
money -= 1000;
System.out.println(Thread.currentThread().getName() + "取出了1000剩余" + money);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}