Java中的多线程
多线程的最主要的目的:是为了提高程序的执行效率(不是执行速度),提高CPU的利用率,如果两个方法之间不存在因果调用关系,那么可以考虑在一个方法执行时开辟另一个线程执行另一个方法,而不用等第一个方法执行完之后再执行第二个方法
计算机通常会有多个核心,一个核心在同一个时间点(很小的计量单位)只能有一个程序在运行。CPU会在高速的情况下,不断的进行切换。线程是一个进程内的独立的执行单元(一个进程至少包含一个线程)。
多线程的实现方案
创建新执行线程有两种方法。
- 但最终都是使用start()方法来启动新线程,
1. 一种方法是将类声明为 Thread 的子类。
该子类应重写 Thread 类的 run 方法。
- 一个类只能调用一次start()方法
- 线程可以指定名字
- 默认无参的构造方法,会产生默认的名字。
- 构造时传递名字
public final String getName()
,使用getname()方法
获取线程名称
代码实现:
class MyThread extends Thread{
@Override
public void run() {
//方法体中的代码,就是你将在新的线程中执行的代码内容.
System.out.println("这是子线程中干的事情");
}
}
public class TestThread {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
class MyThread extends Thread{
@Override
public void run() {
System.out.println("新线程");
}
}
线程休眠 sleep()
public class TestThread {
public static void main(String[] args) {
MyThread myThread = new MyThread("线程一");
System.out.println(myThread.getName());
try {
myThread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myThread.start();
}
}
class MyThread extends Thread{
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.getName());
}
}
2. 实现 声明Runnable 接口的类
Runnable 接口中只有一个run方法,优点是解决当一个类继承了Thread后无法继承其他类的问题
使用方法:
- 实现Runnable接口
- 重写run()方法
- 创建runnable实例
- 创建Thread实例
- 将Runnable实例放入Thread实例中
- 通过线程实例控制线程的行为(运行,停止),在运行时会调用Runnable接口中的run方法。
获取线程名称:Thread.currentThread().getName()
详见4.2实现Runnable接口卖票
3.(有一点问题,不确定Runnable中是否开了多个线程)彻底理解Runnable和Thread的区别
4.多线程卖票案例
4.1继承Thread类卖票
存在重复卖票和,0票和负票的情况
重复票:使用了count = count-1; 应该使用count–;
负票: 在一个线程进行的过程中,如果休眠了,计算机调用另一个线程开始,票数为1条件满足,线程二进入休眠,此时线程一开始卖票并更新票数为0,线程二卖票时就卖出了0票,当还有线程时就会卖出负票。
0票和负票的解决是使用锁,在一个线程进行的时候,对count上锁,暂时禁止共享,用到的是同步代码块
同步代码块(锁)
如果发现没有锁住想要的,那么一定是多个线程之间锁的对象Object不一致
Synchronized(锁对象){
需要同步的代码块
}
卖票的代码具体实现
public class MaiPiao {
public static void main(String[] args) {
MyThread mythread1 = new MyThread("窗口一");
MyThread mythread2 = new MyThread("窗口二");
MyThread mythread3 = new MyThread("窗口三");
Mythread1.start();
Mythread2.start();
Mythread3.start();
}
}
class MyThread extends Thread {
//这里必须是静态变量,让线程可以共享修改变量
static int total = 100;
//对象也必须是静态的,否则synchronized锁的不是同一个对象
private static Object lockobject = new Object();
public myThread(String name) {
super(name);
}
@Override
public void run() {
while (total > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockobject) {
if (total > 0) {
System.out.println(this.getName() + "卖出第" + (total--) + "张票");
} else {
break;
}
}
}
}
}
4.2实现Runnable接口卖票
用到了同步方法
当一个方法都要用synchronized锁起来时可以不用创建新对象,直接在方法前加synchronized关键字修饰
package com.example.demo;
public class TestRunable {
public static void main(String[] args) {
//实例化MyRunnable对象
MyRunnable myRunnable = new MyRunnable();
//实例化Thread对象,即线程
Thread thread1 = new Thread(myRunnable,"线程一");
Thread thread2 = new Thread(myRunnable,"线程二");
Thread thread3 = new Thread(myRunnable,"线程三");
//启用线程
thread1.start();
thread2.start();
thread3.start();
}
}
class MyRunnable implements Runnable{
static int count = 100;
@Override
//同步方法
public void run() {
while (count >0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (sale()){
}else {
break;
}
}
}
public synchronized Boolean sale(){
if (count>0){
System.out.println(Thread.currentThread().getName()+"卖出第"+(count--)+"张票");
return true;
}else {
return false;
}
}
}