多线程
基本概念:
在多线程中有许多基本概念,因此我们先对每个概念做出解释。
进程:
操作系统中独立的运行程序,如QQ,音乐播放器等。
进程的特性:
- 独立性。每个进程之间是相互独立的、互不影响的。
- 互斥性。每个进程(程序)系统都会分配一个独立的端口号,一旦该端口号被占用,那么其他进程就无法使用该端口号。
线程:
进程是由一个或者多个线程组成的,每个进程至少有一个线程支撑。一个线程就是一个小的功能,程序就是由一个个功能组成的
线程的特性:
- 抢占式运行。CPU在执行的时候,是按照时间片来执行的,在单位时间片内,多个线程抢占,谁抢到这个时间片就属于谁。
- 资源共享。一个线程可以共享当前CPU、网卡等。
并行:
同时发生,轮流交替执行。
并行指两个或两个以上事件或活动在同一时刻发生,在多道程序环境下,并行使多个程序同一时刻可在不同CPU上同时执行。
并发:
真正意义上的同时执行。
并发是在同一个cpu上同时(不是真正的同时,而是看来是同时,因为CPU要在多个程序之间切换)运行多个程序。
创建线程的两种方式:
方式1:继承Thread类
步骤:
1、创建一个普通类去
继承Thread类
2、然后
重写run方法
3、
实例化
该子类,并启动线程
package com.lyc.lesson;
class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("吃毛血旺");
}
}
}
class MyThread1 extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("吃水煮肉片");
}
}
}
public class Lesson01 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
MyThread1 myThread1 = new MyThread1();
myThread.start();
myThread1.start();;
for (int i = 0; i < 10; i++) {
System.out.println("吃大盘鸡");
}
}
}
方式2:实现Runable接口
步骤:
1、创建一个普通类去
实现Runable接口
。2、子类中
实现run
方法。3、
实例化子类
4、
new一个Thread的对象
,将实例化子类作为参数
传递,并启动。
package com.lyc.lesson;
class MyThread3 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("线程三:"+i);
}
}
}
class MyThread4 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("线程四:"+i);
}
}
}
public class Lesson02 {
public static void main(String[] args) {
MyThread3 myThread3 = new MyThread3();
MyThread4 myThread4 = new MyThread4();
Thread thread = new Thread(myThread3);
Thread thread1 = new Thread(myThread4);
thread.start();
thread1.start();
for (int i = 0; i < 50; i++) {
System.out.println("主线程一:"+i);
}
}
}
线程中的方法:
static Thread | currentThread() 返回对当前正在执行的线程对象的引用 |
---|---|
String | getName() 返回此线程的名称。 |
void | setName(String name) 将此线程的名称更改为等于参数 name 。 |
int | getPriority() 返回此线程的优先级。 |
void | setPriority(int newPriority) 更改此线程的优先级。 |
设置优先并不一定优先,只是增加了执行的概率。最小值是1,最大值是10,默认的是5 | |
static void | sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 |
方法的实现案例:
package com.lyc.lesson;
class MyThread5 implements Runnable{
@Override
public void run() {
Thread thread = Thread.currentThread();
thread.setName("这是一个重命名的线程");
System.out.println(thread.getName());
System.out.println(thread.getPriority());
}
}
public class Lesson03 {
public static void main(String[] args) {
Thread thread1 = Thread.currentThread(); //获取线程对象
System.out.println(thread1.getPriority()); //获取线程优先级
System.out.println(thread1.getName()); //获取线程名字
// MyThread5 myThread5 = new MyThread5();
// Thread thread2 = new Thread(myThread5);
// Thread thread = new Thread(new MyThread5());
new Thread(new MyThread5()).start();
}
}
sleep()方法:
package com.lyc.lesson;
class MyThread8 implements Runnable{
@Override
public void run() {
Thread thread = Thread.currentThread();
thread.setName("这是已经睡眠过的线程");
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 10; i++) {
System.out.println(thread.getName()+":"+i);
}
}
}
public class Lesson05 {
public static void main(String[] args) {
new Thread(new MyThread8()).start();
Thread thread = Thread.currentThread();
for (int i = 0; i < 10; i++) {
System.out.println(thread.getName()+":"+i);
}
}
}
线程的同步和锁:
为什么要进行线程的同步?
Java是允许多线程的,当多个线程操作同一个资源的时候,就会导致得到的或打印的数据不准确。从而发生冲突。
如下案例:
三个窗口买一百张电影票。
package com.lyc.lesson;
class MySync implements Runnable{
int ticket = 100;
@Override
public void run() {
while (true){
if (ticket >0){
System.out.println(Thread.currentThread().getName()+":"+ticket+"张票");
ticket--;
} else {
System.out.println("买完了");
break;
}
}
}
}
public class Lesson07 {
public static void main(String[] args) {
MySync mySync = new MySync();
Thread thread = new Thread(mySync,"窗口1:");
Thread thread1 = new Thread(mySync,"窗口2:");
Thread thread2 = new Thread(mySync,"窗口3:");
thread.start();
thread1.start();
thread2.start();
}
}
输出结果:
以我们一般的思维来讲,当一个窗口的票卖出去一张票后,其他窗口应该从剩余的票开始卖,但是结果却不是这样,这时就是线程的同步问题。那么该如何解决?加同步锁。
同步代码块:
同步代码块:就是拥有了synchronized 关键字修饰一个语句块。被修饰的语句块会被加锁。从而实现同步。语法格式如下:
synchronized (this) {
被加锁的代码块
}
对上述案例进行修改:
package com.lyc.lesson;
class MySync implements Runnable{
int ticket = 100;
@Override
public void run() {
while (true) {
//同步锁
synchronized (this) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + ":" + ticket + "张票");
ticket--;
} else {
System.out.println("买完了");
break;
}
}
}
}
}
public class Lesson08 {
public static void main(String[] args) {
MySync mySync = new MySync();
Thread thread = new Thread(mySync,"窗口1:");
Thread thread1 = new Thread(mySync,"窗口2:");
Thread thread2 = new Thread(mySync,"窗口3:");
thread.start();
thread1.start();
thread2.start();
}
}
输出结果:
我们对循环进行加锁,这样就能保证在对数据进行操作时,只能是一个线程,谁抢到谁就操作数据,其他线程只能等待。